初始化器到底什么时候被临时销毁?
我今天构建了这个实验,在回答了一些问题之后,
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
。这让我很困惑。我是否错过了一些定义订单的规范文本?
I constructed this experiment today, after answering some question
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
has value false
(resulting from zero initialization) before setting it to true
by the dynamic initialization. If the temporary is destroyed before initialization of b
finished, we will print false
, otherwise true
.
The spec says that the temporary is destroyed at the end of the full-expression. That does not seem to be ordered with the initialization of b
. So I wonder
- Does the spec allow an implementation to print both
false
andtrue
in different runs?
Clang prints false
for the above, while GCC prints true
. This confuses me. Did I miss some spec text defining the order?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为它可以打印出 true 或 false,或者出于某种不相关的原因,什么也不打印。
正确或错误的部分是(正如您所说的),临时
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 中变得简单了多少) 。
I think it's allowed to print out true, or false, or for somewhat unrelated reasons, nothing at all.
The true or false part is (as you've said), that the destruction of the temporary
A
object is not ordered with respect to the dynamic initialization ofb
.The nothing at all possibility is because the initialization of
b
is not ordered with respect to the creation/initialization ofstd::cout
; when you try to destroy the temporary,cout
may not have been created/initialized yet, so attempting to print something may not work at that point at all. [Edit: this is specific to C++98/03, and does not apply to C++11.]Edit: here is how I, at least, see the sequence:
Edit2: After rereading §12.2/4 (yet again), I've changed the diagram again. §12.2/4 says:
I believe this expression is an initializer for a declarator defining an object, so it's required to initialize the object from a copy of the value of the expression (
true
, in this case), not directly from the return value. In the case oftrue
, this is probably a distinction without a difference, but I think the diagram is technically more accurate as it stands right now.This also makes fairly clear (I think) that the temporary holding
true
does not have to be destroyed at the end of the full expression, so I've re-drawn the diagram to reflect that as well.This section is gone in C++0x/C++11, so I've re-drawn the diagram (yet again) to show the difference between the two (and how much simpler this piece has gotten in C++11).
(引用 C++03 标准)
首先是 §12.2/3:
我相信这是一个转移注意力的话题,因为 §1.9/13:
这对我来说意味着
A(b).yield()
本身就是一个完整的表达式,使得 §12.2/3 在这里无关紧要。然后我们进入序列点——§1.9/7:
§1.9/16:
和§1.9/17:
将它们放在一起,我认为 Clang 是正确的,GCC (和 MSVC 2010 SP1)是错误的——保存表达式结果的临时变量(其生命周期根据 §12.2/4 延长)是从
A:: 返回的
,而不是调用bool
yield()yield
的临时A
。考虑到§1.9,在调用A::yield()
之后应该有一个序列点,在此期间临时A
被销毁。(Quoting the C++03 standard)
First there's §12.2/3:
I believe this is a red herring, because of §1.9/13:
This implies to me that
A(b).yield()
is itself a full expression, rendering §12.2/3 irrelevant here.Then we get into sequence points -- §1.9/7:
§1.9/16:
and §1.9/17:
Putting it all together, I think Clang is right and GCC (and MSVC 2010 SP1) is wrong -- the temporary that holds the result of the expression (whose lifetime is being extended as per §12.2/4) is the
bool
returned fromA::yield()
, not the temporaryA
on whichyield
is invoked. Taking into account §1.9, there should be a sequence point after the call toA::yield()
during which the temporaryA
is destroyed.首先,为了澄清之前的段落,在其自己的(动态)初始化中使用
b
并不是 UB。在计算表达式之前,b
不是未初始化而是零初始化。临时
A
的生存时间必须与完整表达式一样长:bool b = A(b).yield();
行是一个声明,是一个语句,而不是一个表达式。手头的表达式只能在=
的右侧找到。 [ISO/IEC 14882:2003(E) A.6]这意味着临时文件应该在动态初始化发生之前被销毁,不是吗?当然,值
true
会保存在包含表达式 1 结果的临时变量中,直到初始化完成,但原始的A
临时变量应该在b
实际修改之前被销毁。因此,我每次都期望输出
false
。1
First, just to clear up the paragraph that was previously here, using
b
in its own (dynamic) initialisation here is not UB. Before the expression is evaluated,b
is not uninitialised but zero-initialised.The temporary
A
must live for precisely as long as the full expression:The line
bool b = A(b).yield();
is a declaration, which is a statement, which is not an expression. The expression at hand is found only to the RHS of the=
. [ISO/IEC 14882:2003(E) A.6]This would mean that the temporary should be destroyed before the dynamic initialisation takes place, no? Sure, the value
true
is held in the temporary that contains the result of the expression1 until the initialisation completes, but the originalA
temporary should be destroyed beforeb
is actually modified.Therefore I'd expect the output
false
, every time.1