右值,左值和形式定义

时间:2019-06-22 15:16:13

标签: c++ c++11 reference language-lawyer value-categories

人们在

中听到这些消息时会感到困惑
int&& x

x具有右值引用类型,但是x是左值。误解源于标识符和表达式是不同的事物,类型和值类别也是如此。此外,表达式的类型“在进行任何进一步分析之前已进行了调整”,并且单词“ rvalue”和“ lvalue”可以出现在类型名称和值类别名称中。

我想澄清正式定义。假设我们有一个函数:

1 | void f(int&& x) {           
2 |     ... = x;               
3 |     ... = std::move(x);
4 | }

以下陈述正确吗?

  1. 在第1行中,x是用于命名函数参数的标识符(id表达式)。其类型为int&&,这是decltype(x)返回的类型。 x不是表达式,没有值类别。
  2. 在第2行中,x是一个表达式。在类型调整之前,其类型为int&&,在类型变为int之后。值类别为左值。
  3. 在第3行中,std::move(x)是一个表达式。调整之前的类型为int&&,之后为-int。值类别为xvalue。
  4. 当我们说x具有右值引用类型时,在类型调整之前,我们将x的类型称为标识符,或者将x的类型称为表达式。
  5. cppreference.com中的语句“每个表达式都有一些非引用类型,每个表达式恰好属于三个主要值类别之一”中的单词“类型”指的是类型调整后的类型。 / li>
  6. 当Scott Meyers writes“如果表达式的类型是左值引用(例如T&const T&等),则该表达式是左值。”他是指调整前的类型,第二个单词“左值”是指值类别。

1 个答案:

答案 0 :(得分:9)

一些初步段落:

  

[基本]

     

3实体是值,对象,引用,函数,枚举器,   类型,类成员,模板,模板专业化,名称空间,   参数包或这个。

     

[dcl.type.simple]

     

4decltype(e)表示的类型定义如下:

     
      
  • 如果e是未括号的 id-expression 或未括号的类成员访问权限([expr.ref]),则decltype(e)是   由e命名的实体。如果没有这样的实体,或者e命名一个集合   的重载函数,程序格式错误;

  •   
  • 否则,如果e是xvalue,则decltype(e)T&&,其中Te的类型;

  •   
  • 否则,如果e是左值,则decltype(e)T&,其中Te的类型;

  •   
  • 否则,decltype(e)e的类型。

  •   
     

[dcl.ref]

     

1在声明T D中,其中D具有以下两种形式之一

& attribute-specifier-seqopt D1 
&& attribute-specifier-seqopt D1
     

,声明T D1中的标识符类型为   “ derived-declarator-type-list T”,然后是标识符的类型   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&或const T&等),则该表达式是左值。”他是指调整前的类型,第二个单词“左值”是指值类别。

不能完全确定Scott Meyers在撰写本文时的意思,但这是对与标准相符的单词的唯一解释,是的。