c++11-17 模板核心知识(九)—— 理解decltype与decltype(auto)

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

decltype介绍

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

咱们先从正常状况开始:express

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对应索引的值:ui

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.code

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使用模板参数推导规则时,引用会被忽略,因此下面的代码会报错:ci

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不表明参数参数推导:get

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):it

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 << '\n';
 
    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 << '\n';
}

(完)

朋友们能够关注下个人公众号,得到最及时的更新: