支撑初始化列表中的对象创建顺序

时间:2014-01-20 12:38:25

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

#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;
}

我有两个问题:

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

编辑:我用msvs 2013编译了它

3 个答案:

答案 0 :(得分:21)

在第二个例子中,你实际上只是用B()初始化;通过使用逗号运算符A()被构造并首先丢弃。

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

另一方面,在第一个实例中,您通过初始化列表从两个临时值初始化C,其元素也应从左到右进行评估,碰巧,但是你的编译器在这方面存在问题:

  

[C++11: 8.5.4/4]: braced-init-list 初始化列表中,初始化子条款,包括任何包扩展(14.5.3)的结果,按照它们出现的顺序进行评估。也就是说,每个值计算和副作用与给定的初始化子句相关联在 initializer-list 的逗号分隔列表中跟随它的任何 initializer-clause 相关联的每个值计算和副作用之前排序。 [注意:无论初始化的语义如何,此评估顺序都成立;例如,当 initializer-list 的元素被解释为构造函数调用的参数时,它适用,即使通常对调用的参数没有排序约束。 -end note]

我可以用GCC 4.8 * 重现问题,但Clang 3.5表现正常。该错误已在之前的 std-discussion 列表中讨论过,但我还没有找到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
https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/TQUnBFkUBDg
§ #51253可能是相关的。

答案 1 :(得分:5)

  

为什么第一个支撑的初始化列表对象是按从右到左的顺序创建的?

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

  

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

相同。左到右。在这种情况下,逗号运算符进入图片,它从左到右评估操作数。

答案 2 :(得分:2)

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

虽然它是用俄语写的,但你可以使用google service translate翻译成英文。

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

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