7

Scott Meyers的《 Effective C++11》草稿指出:

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

Object obj(args...)和有什么区别Object obj{args...}?以及斯科特为什么这么说。

更新:

问题如何使用 C++11 统一初始化语法?问如何,而这个问题问为什么。

更新2:

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

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

4

4 回答 4

6

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

第一个是直接初始化,第二个是直接列表初始化。这在两个不同的部分中提到:

§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]

列表初始化是从一个花括号初始化列表初始化一个对象或引用。这样的初始化器称为初始化器列表,列表中以逗号分隔的初始化器子句称为初始化器列表的元素。初始化列表可能为空。列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为直接列表初始化,复制初始化上下文中的列表初始化称为复制列表初始化


两者之间有一些区别:

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

  • 直接列表初始化不允许缩小参数列表中的转换。§8.5.4/3 [dcl.init.list]

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

  • 括号初始化列表的元素的求值顺序是从左到右。§8.5.4/4 [dcl.init.list]

  • 您可以通过使用直接列表初始化来避免最麻烦的解析

 

  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{}};
于 2014-07-13T07:03:11.653 回答
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> )

}
于 2014-07-13T06:59:59.030 回答
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...}形式:

在花括号初始化列表的初始化列表中,初始化子句,包括由包扩展 (14.5.3) 产生的任何初始化子句,按照它们出现的顺序进行评估。也就是说,与给定初始化子句相关联的每个值计算和副作用在初始化器列表的逗号分隔列表中与任何初始化子句相关联的每个值计算和副作用之前进行排序。 [注意:这无论初始化的语义如何,求值顺序都成立;例如,它适用于初始化器列表的元素被解释为构造函数调用的参数时,即使调用的参数通常没有顺序约束。——尾注]

这意味着:

 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 和 4.7.2)有一个错误,因为该{}表单无法按应有的方式工作。我不确定它是否已在当前版本中修复。

希望有帮助。

于 2014-07-13T06:25:29.240 回答
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
于 2014-07-13T07:14:36.613 回答