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&
对于模板函数的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
的返回值,就跟函数体的实际返回值类型一致了;
int v;
const int& cv = v;
auto av = cv; // av => int
decltype(auto) dav = cv; // dac => const int&
基本上,对于名称以外的表达式,含有类型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 , 以上规则符合除名称以外的表达式
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. 临时变量也是可以返回引用的;(这会造成一些不稳定的因素,要避免);