zoukankan      html  css  js  c++  java
  • c++11-17 模板核心知识(九)—— 理解decltype与decltype(auto)

    与模板参数推导和auto推导一样,decltype的结果大多数情况下是正常的,但是也有少部分情况是反直觉的。

    decltype介绍

    给定一个name或者expression,decltype会告诉你它的类型。

    我们先从正常情况开始:

    const int i = 0;            // decltype(i) is const int
    bool f(const Widget& w);    // decltype(w) is const Widget&
                                // decltype(f) is bool(const Widget&)
    
    struct Point {
      int x, y;         // decltype(Point::x) is int
    };          // decltype(Point::y) is int
    
    Widget w;        // decltype(w) is Widget
    if (f(w)) …       // decltype(f(w)) is bool
    
    template<typename T>    // simplified version of std::vector
    class vector {
    public:
    …
    T& operator[](std::size_t index);
    …
    };
    
    vector<int> v;      // decltype(v) is vector<int>
    …
    if (v[0] == 0) …      // decltype(v[0]) is int&
    

    很直观,没有例外情况。 注意:decltype与auto不同,不会消除const和引用。

    为什么需要decltype

    比如我们需要声明一个函数模板,函数的返回值类型依赖函数参数的类型。在C++11中,常见的例子是返回一个container对应索引的值:

    template <typename Container, typename Index> // works, but requires refinement
    auto authAndAccess(Container &c, Index i) -> decltype(c[i]) {
      return c[i];
    }
    

    注意:这里的auto跟类型推导没有任何关系,它只是表明了这里使用了C++11的trailing return type.

    decltype(auto)

    在C++11中只允许单语句的lambda表达式被推导,在C++14中之中行为被拓展到所有lambda和所有函数,包括多语句。在C++14中,上述代码我们可以简写为:

    template<typename Container, typename Index>        // C++14;  not quite correct
    auto authAndAccess(Container& c, Index i) { 
      return c[i];         // return type deduced from c[i]
    }
    

    注意:这里的auto就跟类型推导有关系了。 在前面讲auto推导规则的文章中提到过,auto作用在函数返回值时,使用的是模板参数推导规则,这里就会出现问题:operator []我们希望它返回引用,但是使用auto使用模板参数推导规则时,引用会被忽略,所以下面的代码会报错:

    template <typename Container, typename Index>
    auto authAndAccess(Container &c, Index i) {
      return c[i];
    }
    
    std::vector<int> v{1,2,3,4,5};
    authAndAccess(v,2) = 10;      // error: expression is not assignable
    

    但是使用auto -> decltype()则不会报错,因为这里auto不代表参数参数推导:

    template <typename Container, typename Index>
    auto authAndAccess(Container &c, Index i) -> decltype(c[i]) {
      return c[i];
    }
    
    std::vector<int> v{1,2,3,4,5};
    authAndAccess(v,2) = 10;
    

    所以,要想让authAndAccess在使用auto的情况下返回引用,在C++14中,我们可以使用decltype(auto):

    template <typename Container, typename Index>
    decltype(auto) authAndAccess(Container &c, Index i) {
      return c[i];
    }
    
    
    std::vector<int> v{1,2,3,4,5};
    authAndAccess(v,2) = 10;
    

    decltype(auto)中的auto代表返回值需要被自动推导,decltype代表使用decltype来推导返回值类型。

    decltype(auto)不仅可以声明函数返回值,还可以声明变量:

    Widget w;
    
    const Widget& cw = w;       // auto type deduction : myWidget1's type is Widget
    
    decltype(auto) myWidget2 = cw;       // decltype type deduction : myWidget2's type is const Widget& 
    

    注意(entity)

    decltype的规则可以看官网:decltype specifier,概况下可以分为两大类:

    • decltype ( entity ) : 如果entity是一个不被括号包围的标识符、类访问表达式,那么decltype ( entity )与entity类型一致。
    • decltype ( expression ) : 如果expression是一个表达式,计算结果为类型T,那么:
      • 如果expression为xvalue,那么decltype的结果是T&&.
      • 如果expression为lvalue,那么decltype的结果是T&.
      • 如果expression为prvalue,那么decltype的结果是T.

    注意第一点中强调了entity是一个不被括号包围的标识符。因为当一个标识符被括号包围时,它就是一个左值表达式了,对应上面第二大点的第二小点。比如说int x = 0;,x是一个标识符,所以decltype(x)的结果为int。但是(x)就是一个左值表达式,decltype((x))的结果就是int&。所以下面的用法是不同的:

    decltype(auto) f1() {
    int x = 0;
        …
        return x;       // decltype(x) is int, so f1 returns int
    }
    
    decltype(auto) f2() {
        int x = 0;
        …
        return (x);     // decltype((x)) is int&, so f2 returns int&
    }
    

    官网的例子能很好的概况decltype最常见的用法:

    #include <iostream>
     
    struct A { double x; };
    const A* a;
     
    decltype(a->x) y;       // type of y is double (declared type)
    decltype((a->x)) z = y; // type of z is const double& (lvalue expression)
     
    template<typename T, typename U>
    auto add(T t, U u) -> decltype(t + u) // return type depends on template parameters
                                          // return type can be deduced since C++14
    {
        return t + u;
    }
     
    int main() 
    {
        int i = 33;
        decltype(i) j = i * 2;
     
        std::cout << "i = " << i << ", "
                  << "j = " << j << '
    ';
     
        auto f = [](int a, int b) -> int
        {
            return a * b;
        };
     
        decltype(f) g = f; // the type of a lambda function is unique and unnamed
        i = f(2, 2);
        j = g(3, 3);
     
        std::cout << "i = " << i << ", "
                  << "j = " << j << '
    ';
    }
    

    (完)

    朋友们可以关注下我的公众号,获得最及时的更新:

  • 相关阅读:
    本地向GitHub提交403错误解决办法(自己的创建的项目)
    zepto-ajax跨域请求接口 jsonp 静态页面数据展示
    自定义获取url方法
    使用swiper 轮播插件ajax 请求加载图片时,无法滑动问题
    背景透明,文字不透明解决办法
    js DOM Element属性和方法整理----转载
    图像处理作业5——SIFT算法与全景图像生成
    应用连续高斯模糊后得到的σ是多少?
    Pandas学习笔记——综合练习
    Pandas学习笔记5——合并
  • 原文地址:https://www.cnblogs.com/zhangyachen/p/14052194.html
Copyright © 2011-2022 走看看