What's the difference between `Object obj(args…)` and `Object obj{args…}`?

前提是你 提交于 2019-12-21 04:31:22

问题


The draft book Effective C++11 by Scott Meyers states:

Distinguish () and {} when creating objects

What's the difference between Object obj(args...) and Object obj{args...}? and why Scott says so.

Update:

The question How to use C++11 uniform initialization syntax? asks for HOW, and this question asks for WHY.

Update2:

I find the following link is helpful and completely answers this question:

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


回答1:


What's the difference between Object obj(args...) and Object obj{args...}?

The first is direct-initialization while the second is direct-list-initialization. This is mentioned in two different sections:

§8.5/16 [dcl.init]

The initialization that occurs in the forms

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

as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conversions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.

and §8.5.4/1 [dcl.init.list]

List-initialization is initialization of an object or reference from a braced-init-list. Such an initializer is called an initializer list, and the comma-separated initializer-clauses of the list are called the elements of the initializer list. An initializer list may be empty. List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization.


There are a few differences between the two:

  • If the type being constructed has a constructor that takes an initializer_list argument, direct-list-initialization will always favor that constructor. Other constructors will only be considered if the initializer_list constructor is not viable. §13.3.1.7/1 [over.match.list]

  • direct-list-initialization does not allow narrowing conversions within the argument list. §8.5.4/3 [dcl.init.list]

  • If the type being initialized is an aggregate, direct-list-initialization will perform aggregate initialization. §8.5.4/3 [dcl.init.list]

  • The order of evaluation of the elements of a braced-init-list is from left to right. §8.5.4/4 [dcl.init.list]

  • You can avoid the most vexing parse by using 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:


The behavior of Object obj(args...) and Object{args...} depends on the constructors defined in Object.

Take the following example:

#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:


What's the difference between Object obj(args...) and Object obj{args...}?

{args...} will prefer a constructor with an initializer_list over other legal candidates.

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

On the other hand, you give up implicit narrowing.

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:


What's the difference between Object obj(args...) and Object obj{args...}? and why Scott says so.

The difference is that in the former case, the order of evaluation of arguments is unsequenced (i.e unspecified) but in the latter case, the order is left-to-right (i.e in which they appear).

The following text from $5.2.2/8 [expr.call] (n3690) deals with Object(args...) form:

The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered (see 1.9).

And the text from $8.5.4/4 [dcl.init.list] (n3690) deals with Object{args...} form:

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and side effect associated with 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.[ Note: This evaluation ordering holds regardless of the semantics of the initialization; for example, it applies when the elements of the initializer-list are interpreted as arguments of a constructor call, even though ordinarily there are no sequencing constraints on the arguments of a call. — end note ]

Well which means this:

 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.

Note that GCC (4.7.0 and 4.7.2) have a bug because of which {} form doesn't work the way it should. I'm not sure if it is fixed in the current version.

Hope that helps.



来源:https://stackoverflow.com/questions/24720028/whats-the-difference-between-object-objargs-and-object-objargs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!