人们在
中听到这些消息时会感到困惑int&& x
x
具有右值引用类型,但是x
是左值。误解源于标识符和表达式是不同的事物,类型和值类别也是如此。此外,表达式的类型“在进行任何进一步分析之前已进行了调整”,并且单词“ rvalue”和“ lvalue”可以出现在类型名称和值类别名称中。
我想澄清正式定义。假设我们有一个函数:
1 | void f(int&& x) {
2 | ... = x;
3 | ... = std::move(x);
4 | }
以下陈述正确吗?
x
是用于命名函数参数的标识符(id表达式)。其类型为int&&
,这是decltype(x)
返回的类型。 x
不是表达式,没有值类别。x
是一个表达式。在类型调整之前,其类型为int&&
,在类型变为int
之后。值类别为左值。std::move(x)
是一个表达式。调整之前的类型为int&&
,之后为-int
。值类别为xvalue。x
具有右值引用类型时,在类型调整之前,我们将x
的类型称为标识符,或者将x
的类型称为表达式。 T&
或const T&
等),则该表达式是左值。”他是指调整前的类型,第二个单词“左值”是指值类别。答案 0 :(得分:9)
一些初步段落:
[基本]
3实体是值,对象,引用,函数,枚举器, 类型,类成员,模板,模板专业化,名称空间, 参数包或这个。
[dcl.type.simple]
4由
decltype(e)
表示的类型定义如下:
如果
e
是未括号的 id-expression 或未括号的类成员访问权限([expr.ref]),则decltype(e)
是 由e
命名的实体。如果没有这样的实体,或者e命名一个集合 的重载函数,程序格式错误;否则,如果
e
是xvalue,则decltype(e)
是T&&
,其中T
是e
的类型;否则,如果e是左值,则
decltype(e)
是T&
,其中T
是e
的类型;否则,
decltype(e)
是e
的类型。[dcl.ref]
1在声明
T D
中,其中D
具有以下两种形式之一& attribute-specifier-seqopt D1 && attribute-specifier-seqopt D1,声明
T D1
中的标识符类型为 “ derived-declarator-type-listT
”,然后是标识符的类型D
中的“是对T
的 derived-declarator-type-list 引用。”[expr]
5如果表达式最初的类型为“对
T
的引用” ([dcl.ref],[dcl.init.ref]),将类型调整为T
之前的任何类型 进一步分析。表达式指定对象或功能 由参考表示,并且表达式是左值或 xvalue,具体取决于表达式。[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在撰写本文时的意思,但这是对与标准相符的单词的唯一解释,是的。