【问题标题】:What's the difference between `Object obj(args...)` and `Object obj{args...}`?`Object obj(args...)` 和 `Object obj{args...}` 有什么区别?
【发布时间】:2014-09-03 09:29:11
【问题描述】:

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

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

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

更新:

问题How to use C++11 uniform initialization syntax? 询问HOW,而这个问题询问WHY。

更新2:

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

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

【问题讨论】:

  • @MattMcNabb,这个问题问的是如何,这个问题是问为什么。
  • 这个问题不问“WHY”,另一个问题涉及统一初始化的作用。
  • 一种是直接初始化,一种是列表初始化。该标准包含大量关于它们的具体信息。
  • @MattMcNabb,我引用了 Scott 的声明,想知道他为什么这么说。

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


【解决方案1】:

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

第一个是direct-initialization,第二个是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]

List-initialization 是从braced-init-list 初始化对象或引用。这样的初始化器称为初始化器列表,列表中以逗号分隔的初始化器子句称为初始化器列表的元素。初始化列表可能为空。 列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为 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{}};

【讨论】:

    【解决方案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> )
    
    }
    

    【讨论】:

      【解决方案3】:

      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
      

      【讨论】:

        【解决方案4】:

        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),按照它们出现的顺序进行评估。那是, 与给定关联的每个值计算和副作用 初始化子句在每个值计算之前排序,并且 与任何跟在它后面的初始化子句相关的副作用 初始化器列表的逗号分隔列表。 [注意:这 无论语义如何,求值顺序都成立 初始化;例如,它适用于 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。我不确定它是否在当前版本中修复。

        希望对您有所帮助。

        【讨论】:

          猜你喜欢
          • 2013-10-20
          • 2014-05-06
          • 1970-01-01
          • 1970-01-01
          • 2013-05-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-11
          相关资源
          最近更新 更多