用像这样的代码
#include <stdio.h>
struct P2d {
double x, y;
P2d(double x, double y) : x(x), y(y) {}
~P2d() { printf("Destructor called\n"); }
};
P2d center() {
return P2d(10, 10);
}
int main(int argc, const char *argv[]) {
const double& x = center().x;
printf("x = %.18g\n", x);
return 0;
}
g++
(版本5.2.0)将在输入in之前破坏该P2d
临时实例,但是无论如何该值都将保留(即,不是绑定到该临时实例的实际成员,而是将创建另一个临时实例来复制该成员的值)。printf
main
x
P2d
double
clang++
(IMO正确)的临时的寿命,而不是延伸P2d
实例的寿命x
基准和析构函数将因此被称为后的printf
在main
。
如果您创建一个类(例如),而不是使用普通double
类型作为x
和y
成员,Double
则两个编译器都同意,并且它们将临时P2d
对象的生存期延长到printf
。
这是一个错误g++
还是标准所允许的?
这由CWG 1651涵盖:
问题616和1213的解决(使成员访问或下标表达式应用于prvalue的结果成为xvalue)意味着将引用绑定到此类临时对象的子对象不会延长临时对象的寿命。12.2 [class.temporary]应该进行修改以确保确实如此。
现状是,仅将prvalues视为引用临时对象-因此[class.temporary] / 5(“第二个上下文是将引用绑定到临时对象时的情况。”)不适用。但是,Clang和GCC尚未实际实现问题616的解决方案。center().x
被视为prvalue。我最好的猜测:
GCC根本没有对任何灾难响应做出任何反应。使用时不延伸寿命标量子对象,因为这些是不被覆盖的[dcl.init.ref] /(5.2.1.1)†。因此,完整的临时对象不需要继续存在(请参见aschelper的答案),并且也不需要,因为引用不会直接绑定。如果子对象是类或数组类型,则引用将直接绑定,并且GCC会延长临时对象的生存期。在DR 60297中已注意到这一点。
Clang识别成员访问权限,并已经实施了“新的”生存期扩展规则-甚至可以处理演员表。从技术上讲,这与其处理价值类别的方式不一致。但是,这更明智,并且一旦解决了上述灾难恢复,它将是正确的行为。
因此,我要说的是,GCC按目前的措词是正确的,但目前的措词是有缺陷且含糊不清的,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会充分考虑到这一点,并将延长生存期。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句