650*_*502 24 c++ reference temporary object-lifetime language-lawyer
用代码就好
#include <iostream>
struct P {
int x;
P(int x) : x(x) {}
~P() { std::cout << "~P()\n"; }
};
int main() {
auto const& x = P{10}.x;
std::cout << "extract\n";
}
Run Code Online (Sandbox Code Playgroud)
GCC打印~P() extract,表示临时的生命周期未被引用扩展.
相比之下,Clang(IMO正确)将临时的生命周期延长到引用的生命周期,x因此析构函数将在输出后调用main.
请注意,如果我们int使用某种类型(例如string),GCC会突然显示Clang的行为.
这是GCC中的错误还是标准允许的内容?
Col*_*mbo 12
CWG 1651涵盖了这一点:
问题616和1213的解决方案,将成员访问或下标表达式的结果应用于prvalue和xvalue,意味着绑定对临时的这个子对象的引用不会延长临时的生命周期.12.2 [class.temporary]应该修改以确保它确实如此.
现状是只有prvalues被视为指临时数 - 因此[class.temporary]/5("第二个上下文是指引用绑定到临时值.")不适用.但是,Clang和GCC实际上没有实施问题616的决议.center().x两者都被视为一个prvalue.我最好的猜测:
GCC根本没有对任何DR做出反应.使用时不延伸寿命标量子对象,因为这些是未覆盖的[dcl.init.ref] /(5.2.1.1)†.因此,完整的临时对象不需要依赖(参见aschelper的答案),但它没有,因为引用不直接绑定.如果子对象是类或数组类型,则引用直接绑定,GCC延长临时生命周期.这在DR 60297中已经注意到.
Clang已经识别成员访问并且已经实现了"新的"生命周期扩展规则 - 它甚至可以处理强制转换.从技术上讲,这与处理价值类别的方式不一致.但是,一旦上述DR得到解决,它就更加明智并且将是正确的行为.
因此,我会说目前的措辞是海湾合作委员会是正确的,但目前的措辞有缺陷和含糊不清,而且Clang已经实施了对DR 1651的待决决议,即N3918.本文非常清楚地介绍了这个例子:
如果
E1是临时表达式并且E2未指定位字段,那么E1.E2它是临时表达式.
center()根据论文[expr.call]/11的措辞,它是一个临时表达式.因此,上述[class.temporary]/5中修改后的措辞适用:
第二个上下文是引用不直接绑定(8.5.3 dcl.init.ref)或使用临时表达式初始化(第5节).相应的临时对象(如果有)在引用的生命周期内持续存在,除了:[...不适用的异常...]
Voilà,我们终身延伸.注意"相应的临时对象"不够清楚,这是提案推迟的原因之一; 一旦修改,它肯定会被采纳.
†
是一个xvalue(但不是一个位字段),类prvalue,数组prvalue或函数lvalue和"
cv1 T1"是引用兼容的"cv2 T2",或[...]
实际上,如果子对象具有数组类型,GCC将完全尊重这一点并延长生命周期.
我会争论g ++中的错误,因为引用N3242草案,§12.2/ 5:
第二个上下文是引用绑定到临时的.绑定引用的临时对象或绑定引用的子对象的完整对象的临时对象在引用的生命周期内持续存在,除了:
所以它的寿命必须延长,除非:
临时绑定到构造函数的ctor-initializer中的引用成员[..]
临时绑定到函数调用[...]中的引用参数
在函数返回语句[...]中临时绑定到返回值的生命周期
临时绑定到
new-initializer[..]中的引用
我们的案例不适合任何这些例外,因此它必须遵循规则.我说g ++在这里是错误的.
然后,关于从同一草案§8.5.3/ 5(强调我的)中提出的报价aschepler:
参考输入" CV1
T1"是通过类型"的表达初始化CV2T2"如下:
如果引用是左值引用和初始化表达式
一个.是左值(但不是位字段)以及" CV1
T1"是参考兼容" CV2T2",或湾 有班级类型......
然后 ...
否则,引用应是对非易失性const类型的左值引用(即,cv1应为
const),或者引用应为右值引用.一个.如果是初始化表达式
一世.是一个x值,类prvalue,阵列prvalue或功能左值和" CV1
T1"是参考兼容" CV2T2",或II.有班级类型......
然后引用绑定到第一种情况下的初始化表达式的值 ....
湾 否则,将使用非引用复制初始化(8.5)的规则从初始化表达式创建并初始化类型为" cv1
T1" 的临时类型.然后将引用绑定到临时.
看看xvalue是什么,这次引用http://en.cppreference.com/w/cpp/language/value_category ...
xvalue("到期值")表达式为[..]
a.m,对象表达式的成员,其中a是rvalue,m是非引用类型的非静态数据成员;
......表达式center().x 应该是 xvalue,因此适用于§8.5.3/ 5的情况2a(而不是副本).我会坚持我的建议:g ++是错误的.
刚看完Columbo的回答.
这是一个gcc bug.相关规则在[class.temporary]中:
有两种情况,临时表在与完整表达结束时不同的地方被摧毁.[...]
第二个上下文是引用绑定到临时的.绑定引用的临时对象或作为绑定引用的子对象的完整对象的临时对象在引用的生存期内持续存在,除了:
- 在函数调用中绑定到引用参数的临时对象(5.2. 2)持续到包含调用的完整表达式完成.
- 不延长函数返回语句(6.6.3)中返回值临时绑定的生存期; 临时在return语句中的full-expression结束时被销毁.
- 在new-initializer(5.3.4)中对引用的临时绑定一直持续到包含new-initializer的full-expression完成为止.
我们绑定了对临时子对象的引用,因此临时应该在引用的生命周期内持续存在.这条规则的三个例外都不适用于此.