`Object obj(args ...)`和`Object obj {args ...}`之间的区别是什么?

时间:2014-07-13 06:03:56

标签: c++ oop c++11 constructor uniform-initialization

Scott Meyers撰写的草案Effective C++11指出:

  

在创建对象时区分()和{}

Object obj(args...)Object obj{args...}之间的区别是什么?为什么斯科特这么说。

更新

问题How to use C++11 uniform initialization syntax?如何询问,这个问题是为什么要求。

UPDATE2:

我发现以下链接很有帮助,完全回答了这个问题:

https://softwareengineering.stackexchange.com/questions/133688/is-c11-uniform-initialization-a-replacement-for-the-old-style-syntax

4 个答案:

答案 0 :(得分:5)

  

Object obj(args...)Object obj{args...}之间有什么区别?

第一个是直接初始化,第二个是 direct-list-initialization 。这在两个不同的部分提到:

§8.5/ 16 [dcl.init]

  

表单中出现的初始化

 T x(a);
 T x{a};
     

以及new表达式(5.3.4),static_cast表达式(5.2.9),功能表示法类型转换(5.2.3)以及基本和成员初始值设定项(12.6.2) )被称为 直接初始化

§8.5.4/ 1 [dcl.init.list]

  

列表初始化是从 braced-init-list 初始化对象或引用。这样的初始化程序称为初始化列表,列表中逗号分隔的 initializer-clauses 称为初始化程序列表的元素。初始化列表可以为空。 列表初始化可以在直接初始化或复制初始化上下文中发生;直接初始化上下文中的列表初始化称为 direct-list-initialization ,复制初始化上下文中的列表初始化称为 copy-list-initialization


两者之间存在一些差异:

  • 如果正在构造的类型具有带initializer_list参数的构造函数, direct-list-initialization 将始终支持该构造函数。只有initializer_list构造函数不可行时才会考虑其他构造函数。 §13.3.1.7/ 1 [over.match.list]

  • direct-list-initialization 不允许缩小参数列表中的转换范围。 §8.5.4/ 3 [dcl.init.list]

  • 如果要初始化的类型是聚合, direct-list-initialization 将执行聚合初始化。 §8.5.4/ 3 [dcl.init.list]

  • braced-init-list 元素的评估顺序是从左到右。 §8.5.4/ 4 [dcl.init.list]

  • 使用 direct-list-initialization

  • 可以避免最烦人的解析

  struct foo{};
  struct bar 
  {    
    bar(foo const&) {}
  };

  bar b1(foo()); // most vexing parse
  bar b2(foo{}); // all 3 of the following construct objects of type bar
  bar b3{foo()};
  bar b4{foo{}};

答案 1 :(得分:2)

Object obj(args...)Object{args...}的行为取决于Object中定义的构造函数。

采用以下示例:

#include <iostream>
#include <initializer_list>

struct A
{
   A(int a, int b) {std::cout << "Came to A::A()\n";}
};

struct B
{
   B(int a, int b) {std::cout << "Came to B::B(int, int)\n";}
   B(std::initializer_list<int> in) {std::cout << "Came to B::B(std::initializer_list<int>)\n";}
};

int main()
{
   A a1(10, 20); // Resolves to A(int, int)
   A a2{10, 20}; // Resolves to A(int, int)
   A a3{30};     // Does not resolve to anything. It's a compiler error.

   B b1(10, 20); // Resolves to B(int, int)
   B b2{10, 20}; // Resolves to B(std::initializer_list<int> )
   B b3{30};     // Resolves to B(std::initializer_list<int> )

}

答案 2 :(得分:1)

  

Object obj(args ...)和Object obj {args ...}之间的区别是什么?为什么斯科特这么说。

不同之处在于,在前一种情况下,论证的评价顺序是无序的(即未指明),但在后一种情况下,顺序是从左到右(即它们出现)。

$ 5.2.2 / 8 [expr.call](n3690)中的以下文字涉及Object(args...)表格:

  

后缀表达式和参数的评估都是相对于彼此的未序列。在输入函数之前,参数评估的所有副作用都会被排序(参见1.9)。

来自$ 8.5.4 / 4 [dcl.init.list](n3690)的文字涉及Object{args...}表格:

  

在braced-init-list的初始化列表中,   initializer-clause,包括包扩展产生的任何条款   (14.5.3),按照它们出现的顺序进行评估。那是,   每个值计算和与给定相关的副作用   initializer-clause在每个值计算之前排序   与其后面的任何初始化子句相关联的副作用   以逗号分隔的初始化列表列表。[注意:这个   无论语义如何,评估排序都成立   初始化;例如,它适用于。的元素   initializer-list被解释为构造函数调用的参数,   即使通常没有排序限制   电话的参数。 - 结束说明]

这意味着:

 int f() { static int i = 10; return ++i; }  //increment the static int!

 Object obj(f(), f()); //is it obj(11,12) or obj(12,11)? Unspecified. 

 Object obj{f(), f()}; //it is obj(11,12). Guaranteed.

请注意GCC (4.7.0 and 4.7.2) have a bug because of which {} form doesn't work the way it should。我不确定它是否在当前版本中修复。

希望有所帮助。

答案 3 :(得分:1)

  

Object obj(args ...)和Object obj {args ...}之间有什么区别?

{args ...}更喜欢带有initializer_list的构造函数而非其他合法候选者。

std::vector<int> v(10); // vector of size 10
std::vector<int> v{10}; // vector initialized with a single element, (int) 10

另一方面,你放弃了隐含的缩小。

std::vector<int> v(10.5); // vector of size 10
std::vector<int> v{10.5}; // illegal - no compile
std::vector<float> v{10.5}; // vector initialized with a single element, (float) 10.5