【问题标题】:Object creation order in braced init list大括号初始化列表中的对象创建顺序
【发布时间】:2014-02-09 15:29:06
【问题描述】:
#include <iostream>

struct A
{
    A() { std::cout << "(A::A)"; }
};

struct B
{
    B() { std::cout << "(B::B)"; }
};

struct C
{
    template<typename ...Args>
    C(Args && ...) {}
};

int main(int agrc, char *argv[])
{
    C {A(), B()}; // <-- prints (B::B)(A::A)
    std::cout << std::endl;
    C {(A(), B())}; // <-- prints (A::A)(B::B)
    std::cout << std::endl;

    return 0;
}

我有 2 个问题:

  • 为什么在第一个大括号初始化列表中,对象是按从右到左的顺序创建的?
  • 为什么第二种情况下的括号会恢复此顺序?

编辑:我已经用 msvs 2013 编译了它

【问题讨论】:

  • 明显重现问题。
  • @dyp C {A(), B()}; // &lt;-- prints (B::B)(A::A) 与你刚才所说的相矛盾吗?
  • 我认为第一行的输出显示了一个错误 - 根据 8.5.4/4,它应该从左到右进行评估。
  • @Borgleader 我想我搞错了:/
  • @dyp。我不这么认为。 In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the comma-separated list of the initializer list.

标签: c++ visual-studio c++11 visual-studio-2013


【解决方案1】:

在第二种情况下,您实际上只是使用B() 进行初始化;通过使用逗号操作符A()首先被构造和丢弃。

C {(A(), B())};
//|^^^^^^^^^^|
//      \--------> (A(), B())
//                  ^^^  ^^^
//                   |    |
//                   /    \
//            evaluated,   THEN evaluated,
//            discarded      used

另一方面,在第一个实例中,您通过初始化列表从 both 临时变量中初始化 C,其元素也应该从左到右进行评估,因为它发生了,但你的编译器在这方面有问题:

[C++11: 8.5.4/4]:braced-init-listinitializer-list 中,initializer-clauses,包括任何由包扩展 (14.5.3),按照它们出现的顺序进行评估。也就是说,与给定 initializer-clause 关联的每个值计算和副作用都是有序的在与 initializer-list 的逗号分隔列表中的任何 initializer-clause 关联的每个值计算和副作用之前。 [注:不管初始化的语义如何,这种求值顺序都成立;例如,当 initializer-list 的元素被解释为构造函数调用的参数时,它适用,即使调用的参数通常没有顺序约束。 ——尾注]

我可以使用 GCC 4.8* 重现该问题,但 Clang 3.5 的行为正常std-discussion 列表中已经讨论过该错误&ddagger;,但我还没有找到 GCC Bugzilla ID§

C {A(), B()};
// ^^^  ^^^
//  |    \
// eval-  THEN
// uated   evaluated
//  \       /
//   \     /
//  both used

*http://coliru.stacked-crooked.com/a/1f18e0d1f8973f3c
http://coliru.stacked-crooked.com/a/5a6e7506e9be97c3
&ddagger;https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/TQUnBFkUBDg
§#51253 可能相关。

【讨论】:

  • @GrijeshChauhan:你喜欢吗?
  • 是的。我喜欢 ASCII 艺术,我也使用tool 来绘制 :)。顺便说一句,恭喜你获得了 100K+,上次我在 98k 左右看到你。
  • @GrijeshChauhan:时间过得真快!
  • #51253 正是这个错误。查看gccclang 的实时示例,取自错误的测试用例。
【解决方案2】:

为什么在第一个大括号初始化列表中对象是按从右到左的顺序创建的?

没有。它是从左到右的。您的编译器有错误,这就是它从右到左评估的原因。 GCC (4.8) 已知有这个错误。你使用 GCC 吗?

为什么第二种情况下的括号会恢复这个顺序?

一样。左到右。在这种情况下,逗号运算符就出现了,它从左到右计算操作数。

【讨论】:

  • 用 msvs 2013 编译
  • @sliser:那也有错误(假设你说的是真的)。​​
【解决方案3】:

这是 gcc 4.8.1(我想您使用 GCC)或其他编译器的旧错误。几个月前我写过这个错误 the initializer-list: a bug of GCC 4.8.1

虽然它是用俄语编写的,但您可以使用例如 google service translate 将其翻译成英文。

正如其他人所说,初始化列表的元素是从左到右评估的,所有副作用都在评估下一个元素之前应用。

在您的第二个代码示例中,您实际上使用一个表达式调用构造函数,该表达式是逗号运算符的表达式。逗号运算符实际上与初始化列表的行为方式相同,即它从左到右计算其操作数,并在计算下一个操作数之前应用副作用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2015-06-04
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 2013-08-23
    • 2016-04-14
    相关资源
    最近更新 更多