【Effective modern C++ 提炼】《类型推导》- Item3:《decltype》

          • 在C++11中,decltype的主要使用目的,及C++14的扩展
          • decltype(auto)在初始化表达式中的应用
          • decltype有关于引用的规律总结
          • 更改decltype所报告的类型
          • Things to Remember

Givent a name or an expression,decltype tells you the name’s or the expression’s type.
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
//...
if (v[0] == 0) ...           // decltype(v[0]) is int&
在C++11中,decltype的主要使用目的,及C++14的扩展

对于模板函数的auto返回值,其需要依赖参数的类型时,可以使用decltype

template<typename Container, typename Index>    
auto
 authAndAccess(Container& c, Index i)       
-> decltype(c[i])
{
  authenticateUser();
  return c[i];
}

std::deque<int> intDeq;
//...
auto val = authAndAccess(intDeq,3); // 编译器会告诉你它的类型(int&)

通过c[i]指定了返回值类型,无论operator[]返回什么类型,都能正确获取到他的类型;而不使用decltype(称为尾随返回类型),在调用时,则无法立即获知他的返回类型;(C++11)

C++11允许推导单语句的lambda表达式的返回值,而到C++14,它扩展到所有的函数,以及lambda表达式;这意味着,可以不用使用这种decltype(即尾随返回类型)的形式,只需前置的auto,也可以获知其返回类型;如下:

template<typename Container, typename Index>    
auto authAndAccess(Container& c, Index i)
{
  authenticateUser();
  return c[i];
}

std::deque<int> intDeq;
//...
auto val = authAndAccess(intDeq,3); // OK!!!编译器会告诉你它的类型(int)

但使用这种方法,在这里有个问题,如下:

template<typename T>
class vector {
public:
  //...
  T& operator[](std::size_t index);
  //...
};

vector<int> intVect;
//...
authAndAccess(intVect, 1) = 10; // 报错!!!试图向右值赋值!!!

intVect[1]返回的时一个int&值,而auto类型推导,将会过滤掉&引用符,返回的是一个int类型的拷贝,即是一个右值(根据no-name规则);

为了使该中间层能正确的返回跟vector::operator[]所返回的完全一样的类型,需要使用decltype类型推导(即开头例子中的尾随返回类型-> decltype(c[i]));
C++14中,还有一种解决方法:decltype(auto);如下:

template<typename Container, typename Index>    
decltype(auto) authAndAccess(Container& c, Index i)
{
  authenticateUser();
  return c[i];
}

vector<int> intVect;
//...
authAndAccess(intVect, 1) = 10; // 没问题!!!

上例中,auto表示:该类型不确定,要使用类型推导(引用被忽略了),然后decltype表示:要使用decltype规则(即使用c[i]实际的类型);从而,authAndAccess的返回值,就跟函数体的实际返回值类型一致了;

decltype(auto)在初始化表达式中的应用
int v; 
const int& cv = v; 

auto av = cv;               // av  => int
decltype(auto) dav = cv;    // dac => const int&
decltype有关于引用的规律总结

基本上,对于名称以外的表达式,含有类型T(即推导的),那么decltype将其报告为T&类型;(因为对于大多数左值表达式,本身遍包含了左值引用限定符,如:返回左值的函数,基本上都会返回其引用)如:

static int g_val = 1;

auto getValue() { return g_val; }
auto& getRefValue() { return g_val; }

decltype(getValue())    => int   ,因为getValue()返回的是一个右值
decltype(getRefValue()) => int & ,因为getRefValue()返回的是一个左值
decltype(g_val)         => int   , 以上规则符合除名称以外的表达式
更改decltype所报告的类型

Putting parentheses around a name can change the type that decltype reports for it!
即,只需要对名称加个括号即可;如下:

static int g_val = 1;

decltype(auto) getValue()    { return g_val; }
decltype(auto) getRefValue() { return (g_val); }
decltype(auto) getRefTmp()   { int a = 0; return (a); }

decltype(getValue())    => int
decltype(getRefValue()) => int &
decltype( (g_val) )     => int &


decltype( (1) )          => int   , 注意!!! 
decltype( (getValue()) ) => int   , 注意!!!
decltype(getRefTmp())    => int & , 注意!!!

decltype( (x) )所需注意的地方:
1. 该形式有个例外,对于右值是无效的;(即无名称的,1、getValue()都没有变量名称);
2. 临时变量也是可以返回引用的;(这会造成一些不稳定的因素,要避免);

Things to Remember
  • decltype报告的类型,几乎就是“名称”和“表达式”的完全实际的类型;
  • 对于左值表达式的类型T(即返回值推导,非名称),decltype总是将其报告为T&;
  • C++14支持decltype(auto);

你可能感兴趣的:(C/C++/C#)