7

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

Community
  • 1
  • 1
xmllmx
  • 39,765
  • 26
  • 162
  • 323
  • possible duplicate of [How to use C++11 uniform initialization syntax?](http://stackoverflow.com/questions/7612075/how-to-use-c11-uniform-initialization-syntax) – M.M Jul 13 '14 at 06:05
  • @MattMcNabb, that question asks for HOW, this question asks for WHY. – xmllmx Jul 13 '14 at 06:08
  • 5
    This question doesn't ask "WHY", and the other question covers what uniform initialization does. – M.M Jul 13 '14 at 06:09
  • One is direct initialization and the other is list initialization. The standard has tons of specific information on both of them. – chris Jul 13 '14 at 06:11
  • @MattMcNabb, I cite Scott's statement and want to know why he says so. – xmllmx Jul 13 '14 at 06:11
  • Oh, why distinguish? Because braces especially can result in something unexpected, mostly due to list constructors. – chris Jul 13 '14 at 06:12
  • You'd have to ask Scott Meyers why his book says what it says. There's a chance that his book explains it – M.M Jul 13 '14 at 06:15

4 Answers4

6

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{}};
Praetorian
  • 106,671
  • 19
  • 240
  • 328
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> )

}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

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.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
1

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
codenheim
  • 20,467
  • 1
  • 59
  • 80