Evg*_*Evg 12 c++ reference language-lawyer c++11 value-categories
人们听到他们的声音感到困惑
int&& x
Run Code Online (Sandbox Code Playgroud)
x具有右值引用类型,但x为左值。误解源于标识符和表达式是不同的事物,类型和值类别也是如此。此外,表达式的类型“在进行任何进一步分析之前已调整”,并且单词“ rvalue”和“ lvalue”可以同时出现在类型名称和值类别名称中。
我想澄清正式定义。假设我们有一个函数:
1 | void f(int&& x) {
2 | ... = x;
3 | ... = std::move(x);
4 | }
Run Code Online (Sandbox Code Playgroud)
以下陈述正确吗?
x是一个标识符(id表达式),它为函数参数命名。其类型为int&&,这是decltype(x)返回的类型。x不是表达式,没有值类别。x是一个表达式。在类型调整之前,其类型为int&&,而在类型变为之后int。值类别为左值。std::move(x)是一个表达式。调整前的类型是int&&-之后int。值类别为xvalue。x具有右值引用类型时,在类型调整之前,我们将其x称为标识符,或者将其x称为表达式。T&或const T&,等等),则该表达式是左值。” 他是指调整前的类型,第二个单词“左值”是指值类别。首先是一些初步的段落:
[基本]
3实体是值,对象,引用,函数,枚举器,类型,类成员,模板,模板专门化,名称空间,参数包或此。
[dcl.type.simple]
4用表示的类型
decltype(e)定义如下:
如果
e是未括号化的id表达式或未括号化的类成员访问([expr.ref]),decltype(e)则是由命名的实体的类型e。如果没有这样的实体,或者如果e命名了一组重载函数,则程序格式错误;否则,如果
e是xvalue,decltype(e)则是T&&,其中T的类型e;否则,如果e是左值,
decltype(e)则isT&,其中T的类型e;否则
decltype(e)为的类型e。[dcl.ref]
1在声明
T D中D具有以下两种形式之一&attribute-specifier-seq opt D1 && 属性说明符-SEQ 选择 D1并且声明中标识符的类型
T D1为“ derived-declarator-type-listT”,则标识符的类型D为“ derived-declarator-type-list引用T。”[expr]
5如果表达式最初的类型为“对的引用
T”([dcl.ref],[dcl.init.ref]),则将该类型调整为T在进行任何进一步分析之前。表达式指定由引用表示的对象或函数,并且表达式取决于表达式是左值还是x值。[expr.prim.general]
8一种标识符是一个ID-表达,只要它已被适当地声明(第[dcl.dcl])。表达式的类型是标识符的类型。结果是由标识符表示的实体。如果实体是函数,变量或数据成员,则结果为左值,否则为prvalue。
[expr.call]
10如果结果类型是左值引用类型或对函数类型的右值引用,则函数调用为左值;如果结果类型是对对象类型的右值引用,则函数调用为xvalue;否则为prvalue。
现在,我们可以回答您的问题。
在第1行中,
x是一个标识符(id表达式),它为函数参数命名。其类型为int&&,这是decltype(x)返回的类型。x不是表达式,没有值类别。
是的。x在声明中不是表达式。但作为论据,decltype 是一种表达。但是,它遇到了decltype第一个项目符号的特殊情况,因此x推导了由命名的标识符的类型,而不是x作为表达式的类型。
第2行
x是一个表达式。在类型调整之前,其类型为int&&,而在类型变为之后int。值类别为左值。
是。
在第3行中,
std::move(x)是一个表达式。调整前的类型是int&&-之后int。值类别为xvalue。
是。
当我们说它
x具有右值引用类型时,在类型调整之前,我们将其x称为标识符,或者将其x称为表达式。
是。
cppreference.com上的语句“每个表达式都有一些非引用类型,每个表达式恰好属于三个主要值类别之一”中的单词“类型”是指类型调整后的类型。
是。
当斯科特·迈耶斯(Scott Meyers)写道:“如果表达式的类型是左值引用(例如
T&或constT&等),则该表达式是左值”。他是指调整前的类型,第二个单词“左值”是指值类别。
斯科特·迈耶斯(Scott Meyers)撰写本文时并不能真正确定是什么意思,但这是对与标准相符的单词的唯一解释,是的。