decltype demystified
读了这个文章,决定做个总结
- decltype is given two entirely different purposes that are related enough to be confusing and lead to bad typos
- Every expression in C++ has both a type and a value category
这里面值类别(value category)是一个在C++标准中也比较混乱的概念(a tangled mess),每个表达式的值类别为这三种之一:lvalue, prvalue, xvalue 不同的built-in操作符会对操作数有要求,并返回不同的值类别。
作者总结了一个概念"expression decltype",几乎每个表达式都有这个属性,且这个概念可以明确地表明表达式的值类别,并且可以通过调整从而仅包含值类别。
First, in some cases (such as casts), the expression decltype is explicit and maybe even textually part of the expression.
Second, the expression decltypes of built-in operators are analogous to the return types of overloaded operators. Thus, programmers accustomed to operator overloading should find them intuitive (except maybe for the conditional ternary operator E1 ? E2 : E3).
Finally, while lvalue, prvalue, and xvalue are kind of abstract concepts, you can use the compiler to check the decltype of an expression
前面提到decltype这个关键词进行两种完全不同的类型计算,这里解释一下,对于decltype(E)
- 如果E是一个没有被括号括起来的id-expression(e.g., x, s.field, S::field,注意非静态成员函数不能用decltype形如(s.method_ptr)),那么decltype(E)返回这些变量、成员变量、非类型模板参数被声明的类型,包括lvalue或者rvalue reference。记这种类型为variable decltype。
- 如果E是别的东西,包括带括号的id-expression(e.g., (x), (s.field)),那么E中任何引用都会被视作透明且不可被检测到,于是decltype(E)取得
E
潜在的非引用类型T
,并且按照后面的规则决定是否返回一个引用:E
是prvalue
则返回T
,E
是lvalue
则返回T&
,E
是xvalue
则返回T&&
。记这种类型为expression decltype
safer use of decltype
可以看到,decltype的使用相当不直观,想要正确地使用它,可以在涉及decltype的地方用一种特定的约束风格去写程序。比如,相比于if (x = y)
,我们应该写if ((x = y))
来表明自己的意图,此外,当你明确需要一个expression decltype时,你可以定义一个宏来帮助自己:
#define exprtype(E) decltype((E))
当你明确不需要expression decltype时,根据你真实的需求,采取不同的做法:
- neither variable nor expression decltype
此时,你可能需要一个变量的非引用类型,不管他本身是什么类型。(auto
的变量或者return type的类型推导就是这样做的)你应该考虑std::decay_t来移除引用,即:
#define autotype(v) std::decay_t<decltype(v)>
- really want variable decltype
此时,你可以定义一个宏如下:
这个宏能在输入带括号的变量时触发编译错误,但是它解决不了 vdecltype(++v) 这种情况(依然会返回引用)
#define IGNORE(x) // causes error if invoked with 2 arguments
#define APPLY_IGNORE(x) IGNORE(x)
#define PARENTHESIZED_TO_COMMA(x) ,
#define vdecltype(v) APPLY_IGNORE(PARENTHESIZED_TO_COMMA v) decltype(v)
it's better to think of value categories as synonymous with reference qualifiers on expression decltypes
接下来作者详细介绍了几种值类型的含义。
-
prvalue
纯右值,这个是最好理解的。通常用来构造变量,在例子auto s = std::string("hello world");
中,prvalue表达式的求值过程本身不会创建对象或调用构造函数,s
会直接由构造函数std::string(const char*)
构造。 -
glvalue
这是程序中的一个实际对象,它有地址。你可以直接给一个glvalue赋值,unless it is a function or a user-defined class with a deleted or inaccessible operator=. -
xvalue("expiring glvalue")
资源即将被移动的对象的值类型。比如,在表达式求值结束就被摧毁的对象(如函数返回值被用来移动构造时)。
std::move transforms its argument into an xvalue -
lvalue
A glvalue that is not an xvalue. The archetypal lvalue expression is a variable, but things that behave like variables are lvalues, too, such as class data members and function calls returning lvalue references. -
rvalue
An rvalue is a prvalue or xvalue.
Why value categories matter
看到这里,后续再来。