Bel*_*loc 3 c++ initialization reference language-lawyer c++14
下面的代码片段汇编
#include <iostream>
int& f() { static int i = 100; std::cout << i << '\n'; return i; }
int main()
{
int& r = f();
r = 101;
f();
}
Run Code Online (Sandbox Code Playgroud)
并打印值(实例)
100
101
Run Code Online (Sandbox Code Playgroud)
现在,阅读N4140中的§8.5.3/ 5,我可以看到它由于子弹点(5.1.1)而编译,也就是说,引用是左值引用,初始化表达式是左值并且与int引用兼容int(或者int&- 我不确定我应该在这里使用哪一个).
子弹点(5.1)和(5.1.1):
- 如果引用是左值引用和初始化表达式
Run Code Online (Sandbox Code Playgroud)— is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or ...
现在假设我int& r = f();通过正确的值引用更改声明中的左值引用,即int&& r = f();.我知道代码不会编译,因为右值引用不会绑定到左值.但我很好奇的是,如何使用标准来达成这个结论?
我会解释一下我的困难:
int&& r = f();由子弹点(5.2)涵盖,因为参考是右值参考.要点(5.2):
- 否则,引用应是对非易失性const类型的左值引用(即,cv1应为const),或者引用应为右值引用.
int与int(或int&)的引用兼容.要点(5.2.1)和(5.2.1.1):
- 如果是初始化表达式
Run Code Online (Sandbox Code Playgroud)— is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or ...
编辑
我已经从N4140(C++ 14)中逐字列出了要点,它们相当于N3337(C++ 11)中类似的要点.
初始化表达式是一个左值并且与
int引用兼容int(或者int&- 我不知道我应该在这里使用哪一个).
引用兼容性是应用于所引用类型的关系,而不是引用类型.例如,[dcl.init.ref]/5讨论初始化"通过类型cv2 T1的表达式对类型cv1的引用 T2 ",并且稍后比较例如"where T1not not related to related T2".
表达式的类型f()只是int,尽管返回类型f是int&.当我们观察它们时,表达式根本就没有引用类型(*) ; 引用被剥离并用于确定值类别(参见[expr]/5).因为int& f(),表达式f()是左值; 因为int g(),表达式g()是一个右值.
(*)为了非常精确,表达式可以在标准中具有引用类型,但仅作为"初始"结果类型.在"任何进一步分析之前"删除引用,这意味着该引用根本不能通过类型观察到.
现在假设我
int& r = f();通过正确的值引用更改声明中的左值引用,即int&& r = f();.我知道代码不会编译,因为右值引用不会绑定到左值.但我很好奇的是,如何使用标准来达成这个结论?
从评论中的讨论看来,混淆似乎f()不是函数左值.诸如"左值"和"右值"之类的值类别是表达式的属性.因此,术语"函数左值"必须引用表达式,即具有值类别"左值"的函数类型的表达式.
但表达式f()是一个函数调用表达式.在语法上,它是一个后缀表达式,后缀是函数参数列表.根据[expr.call]/10:
如果结果类型是左值引用类型或对函数类型的右值引用,则函数调用是左值;如果结果类型是对象类型的右值引用,则为xvalue,否则为prvalue.
和[expr.call]/3
如果postfix-expression指定析构函数[...]; 否则,函数调用表达式的类型是静态选择函数的返回类型[...]
也就是说,表达式的(观察见上文)类型f()是int,并且值类别是"左值".请注意,(观察到的)类型不是 int&.
函数左值是例如一个ID-表达类似f,indirecting函数指针,或产生任何种类的参考函数的表达式的结果:
using ft = void();
void f();
ft& l();
ft&& r();
ft* p();
// function lvalue expressions:
f
l()
r()
*p()
Run Code Online (Sandbox Code Playgroud)
[expr.prim.general]/8指定那些标识符喜欢f是,如ID-表达式,左值:
一个标识符是一个ID-表达,只要它已被适当地声明.[...]表达式的类型是标识符的类型.结果是由标识符表示的实体.如果实体是函数,变量或数据成员,则结果是左值,否则为prvalue.
回到例子int&& r = f();.使用一些后N4296草案.
[dcl.init.ref]
5参考输入" CV1
T1"是通过类型"的表达初始化CV2T2"如下:
- (5.1)如果引用是左值引用和初始化表达式
引用是右值引用.5.1不适用.
- (5.2)否则,引用应是对非易失性const类型的左值引用(即,cv1应为
const),或者引用应为右值引用.[例子省略]
这适用,引用是rvalue-reference.
- (5.2.1)如果是初始化表达式
- (5.2.1.1)是xvalue(但不是位字段),类prvalue,数组prvalue或函数lvalue和[...],或者
- (5.2.1.2)有一个类类型(即
T2类型)[...]
初始化程序是类型的左值int.5.2.1不适用.
- (5.2.2)否则:
- (5.2.2.1)如果
T1或是T2类型[...]- (5.2.2.2)否则,创建一个临时类型" cv1
T1"并从初始化表达式复制初始化(dcl.init).然后将引用绑定到临时.
最后,5.2.2.2适用.然而:
如果
T1与参考相关T2:
- (5.2.2.3)CV1应是相同的CV-资格,或更大的CV-资格比,CV2 ; 和
- (5.2.2.4)如果引用是右值引用,则初始化表达式不应是左值.
T1和T2是int(的返回类型的参考f()被去除并且只用来确定值类别),所以他们参考相关.cv1和cv2都是空的.引用是一个右值引用,并且f()是一个左值,因此5.2.2.4使程序格式错误.
术语"函数左值"出现在5.2.1.1中的原因可能与"函数rvalues"的问题有关(例如,参见N3010 - Rvalue References as"Funny"Lvalues).C++ 03中没有函数rvalues,似乎委员会不想在C++ 11中引入它们.没有右值引用,我认为获得函数rvalue是不可能的.例如,您可能无法转换为函数类型,并且您可能无法从函数返回函数类型.
可能为了一致性,函数左值可以通过强制转换绑定到函数类型的右值引用:
template<typename T>
void move_and_do(T& t)
{
T&& r = static_cast<T&&>(t); // as if moved
}
int i = 42;
move_and_do(i);
move_and_do(f);
Run Code Online (Sandbox Code Playgroud)
但是对于T函数类型来说void(),值类别static_cast<T&&>(t)是lvalue(没有函数类型的rvalues).因此,对函数类型的rvalue引用可以绑定到函数左值.
| 归档时间: |
|
| 查看次数: |
191 次 |
| 最近记录: |