decltype demystified

读了这个文章,决定做个总结

  1. decltype is given two entirely different purposes that are related enough to be confusing and lead to bad typos
  2. 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)

  1. 如果E是一个没有被括号括起来的id-expression(e.g., x, s.field, S::field,注意非静态成员函数不能用decltype形如(s.method_ptr)),那么decltype(E)返回这些变量、成员变量、非类型模板参数被声明的类型,包括lvalue或者rvalue reference。记这种类型为variable decltype。
  2. 如果E是别的东西,包括带括号的id-expression(e.g., (x), (s.field)),那么E中任何引用都会被视作透明且不可被检测到,于是decltype(E)取得E潜在的非引用类型T,并且按照后面的规则决定是否返回一个引用:Eprvalue则返回TElvalue则返回T&Exvalue则返回T&&。记这种类型为expression decltype

safer use of decltype

可以看到,decltype的使用相当不直观,想要正确地使用它,可以在涉及decltype的地方用一种特定的约束风格去写程序。比如,相比于if (x = y),我们应该写if ((x = y))来表明自己的意图,此外,当你明确需要一个expression decltype时,你可以定义一个宏来帮助自己:

#define exprtype(E) decltype((E))

当你明确不需要expression decltype时,根据你真实的需求,采取不同的做法:

  1. neither variable nor expression decltype
    此时,你可能需要一个变量的非引用类型,不管他本身是什么类型。(auto的变量或者return type的类型推导就是这样做的)你应该考虑std::decay_t来移除引用,即:
#define autotype(v) std::decay_t<decltype(v)>
  1. 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

接下来作者详细介绍了几种值类型的含义。

  1. prvalue
    纯右值,这个是最好理解的。通常用来构造变量,在例子auto s = std::string("hello world");中,prvalue表达式的求值过程本身不会创建对象或调用构造函数,s会直接由构造函数std::string(const char*) 构造。

  2. glvalue
    这是程序中的一个实际对象,它有地址。你可以直接给一个glvalue赋值,unless it is a function or a user-defined class with a deleted or inaccessible operator=.

  3. xvalue("expiring glvalue")
    资源即将被移动的对象的值类型。比如,在表达式求值结束就被摧毁的对象(如函数返回值被用来移动构造时)。
    std::move transforms its argument into an xvalue

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

  5. rvalue

An rvalue is a prvalue or xvalue.

Why value categories matter

看到这里,后续再来。

posted on 2024-12-23 19:07  火焰龙卷风  阅读(4)  评论(0编辑  收藏  举报

导航