我在回答了一些问题之后今天构建了这个实验
struct A {
bool &b;
A(bool &b):b(b) { }
~A() { std::cout << b; }
bool yield() { return true; }
};
bool b = A(b).yield();
int main() { }
b
在通过动态初始化将其设置为false
之前具有值true
(由初始化为零)。如果临时在b
初始化之前被销毁,我们将打印false
,否则为true
。
规范说明临时在全表达结束时被销毁。这似乎与b
的初始化无关。所以我想知道
false
和true
? Clang为上面打印false
,而GCC打印true
。这让我很困惑。我是否遗漏了一些定义订单的规范文本?
答案 0 :(得分:8)
我认为允许打印出真实或错误,或者出于某些无关的原因,一无所获。
真或假的部分是(正如你所说的),临时A
对象的破坏与b
的动态初始化无关。
没有任何可能性是因为b
的初始化没有针对std::cout
的创建/初始化进行排序;当您尝试销毁临时文件时,cout
可能尚未创建/初始化,因此尝试打印某些内容可能根本不起作用。 [编辑:这是特定于C ++ 98/03,并不适用于C ++ 11。]
编辑:这是我至少看到序列的方式:
Edit2:在重读§12.2/ 4之后(又一次),我再次更改了图表。 §12.2/ 4说:
有两种情况下,临时表在与完整表达式结束时不同的点被销毁。第一个上下文是表达式作为定义对象的声明符的初始值设定项。在该上下文中,保存表达式结果的临时值将持续存在,直到对象的初始化完成。该对象从临时副本初始化;在此复制过程中,实现可以多次调用复制构造函数;复制后,初始化完成之前或之后,临时文件将被销毁。
我相信这个表达式是定义对象的声明符的初始化器,因此需要从表达式(true
的值)的副本初始化对象,在这种情况下,不是直接从返回值。在true
的情况下,这可能是一个没有区别的区别,但我认为该图在技术上更准确,因为它现在正在。
这也很清楚(我认为)在完整表达式结束时必须销毁临时持有true
不,所以我重新绘制了图也反映了这一点。
这部分已经在C ++ 0x / C ++ 11中消失了,所以我重新绘制了图表(再次)以显示两者之间的差异(以及这篇文章在C +中有多简单) 11)。
答案 1 :(得分:2)
(引用C ++ 03标准)
首先是§12.2/ 3:
当实现引入具有非平凡构造函数(12.1)的类的临时对象时,它应确保为临时对象调用构造函数。类似地,应该使用非平凡的析构函数(12.4)调用析构函数。 临时对象在评估全表达式(1.9)的最后一步时被销毁,该表达式(词法上)包含创建它们的点。即使该评估以抛出异常结束,也是如此。< / p>
我认为这是一个红鲱鱼,因为§1.9/ 13:
[注意:C ++中的某些上下文会导致评估由表达式(5.18)以外的语法结构产生的完整表达式。例如,在8.5中,初始化程序的一种语法是
( expression-list )
但是结果构造是对构造函数的函数调用,其中expression-list作为参数列表;这样的函数调用是一个完整的表达式。例如,在8.5中,初始化程序的另一种语法是
= initializer-clause
但同样结果构造可能是对构造函数的函数调用,其中一个赋值表达式作为参数;再次,函数调用是一个完整表达式。 ]
这对我来说意味着A(b).yield()
本身就是一个完整的表达式,使得§12.2/ 3在这里无关紧要。
然后我们进入序列点 - §1.9/ 7:
访问由volatile lvalue(3.10)指定的对象,修改对象,调用库I / O函数或调用执行任何这些操作的函数都是副作用,这些都是状态的变化。执行环境。表达的评估可能产生副作用。 在执行序列中称为序列点的某些特定点,先前评估的所有副作用都应完整,并且不会产生后续评估的副作用。
§1.9/ 16:
每个完整表达式的评估完成时都有一个序列点。
和§1.9/ 17:
当调用函数时(无论函数是否为内联函数),在评估函数体中任何表达式或语句之前发生的所有函数参数(如果有)之后,都会有一个序列点。 在复制返回值之后和执行函数之外的任何表达式之前,还有一个序列点。
总而言之,我认为 Clang是对的,GCC(和MSVC 2010 SP1)是错误的 - 保存表达式结果的临时表(其生命周期根据§进行扩展) 12.2 / 4)是从bool
返回的A::yield()
,而不是调用A
的临时yield
。考虑到§1.9,在调用A::yield()
之后应该有一个序列点,在此期间临时A
被销毁。
答案 2 :(得分:2)
首先,只是为了清除之前在此处的段落,在其自己的(动态)初始化中使用b
不是UB。在评估表达式之前,b
不是未初始化的,而是零初始化。
临时A
必须与完整表达一样长寿:
临时对象被破坏了 评估的最后一步 完全表达(1.9)(词汇) 包含他们所处的位置 创建
[ISO / IEC 14882:2003(E)12.2 / 3]
行bool b = A(b).yield();
是一个声明,它是一个声明,它不是表达式。只有=
的RHS才能找到手头的表达式。 [ISO / IEC 14882:2003(E)A.6]
这意味着在进行动态初始化之前,临时应该被销毁,不是吗?当然,值true
保存在包含表达式 1 结果的临时值中,直到初始化完成,但原始A
临时值应在{{1}之前销毁实际上是修改过的。
因此,我希望每次输出b
。
1
第一个背景是什么时候 表达式显示为初始化程序 用于定义对象的声明符。 在那种情况下,暂时的 保存表达式的结果 应该坚持到对象为止 初始化完成“
[ISO / IEC 14882:2003(E)12.2 / 4]