3

Before the shouts for duplicate begin: I am aware that the following question (and some others) are quite related to this one:

Is there a difference in C++ between copy initialization and direct initialization?

The answers in this question perfectly explains scenarios where copy initialization is not possible and explains the difference of the two and all that stuff. However, my question is more subtle:

Consider the code:

A createA(){...}

A a1 = createA();
A a2(createA());

Assume that A can be implicitly copy constructed and all that stuff, so both initializations of a1 and a2 are fine. There are no side effects in the copy constructor for A, so both initializations are also semantically equivalent. createA() returns directly the type of the variable, not something else that has to be cast first. I think this case is quite common.

So, in this case, where both alternatives are equally applicable and semantically equivalent, which one should I use? Is there a recommendation in the spec or a consensus/best practice in the community or is it just up to me and my coding style which one to use? Has C++11 introduced any difference in comparison to older standards?

Community
  • 1
  • 1
gexicide
  • 38,535
  • 21
  • 92
  • 152
  • It comes down to style and taste, which you should be concistent about. However, I think there's a good reason for preferring direct initialisation by default. The clue is in the name: use copy initialisation when you want the compiler to look for a conversion sequence. If anything, you can just consider direct initialisation to be friendlier on the compiler. – Joseph Mansfield Feb 25 '13 at 12:51

2 Answers2

2

If everything else is equal (semantics, performance,...) it's obviously only a matter of taste and/or style convention. However, many authors advocate a list-initializer for direct initialization these days:

A a3 {createA()};

But I'd say it's either up to you (if it's your fun project) or to your coworkers to decide on one of the two (three) alternatives. I would not recommend to use both interchangeably, because that would make readers wonder why you use sometimes direct and sometimes copy initialization.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • 1
    What are the arguments for using the list-initializer syntax? – gexicide Feb 25 '13 at 12:49
  • IIRC it's because of a subtle parser difficulty: assume A has a constructor that takes a B and you want to create an A, initialized with a newly constructed B. The intuitive definition would be `A a4(B());` But the parser will see this as a declaration of a function a4, returning an A and taking a function as parameter that returns a B and has no parameters - equivalent to `typedef B Func(); A a4(Func f);`. This can only be worked around by using brace initialization: `A a4 {B()};` – Arne Mertz Feb 25 '13 at 12:53
1

There's no "always better" answer, it's a matter of style.

When initializing an object from a simple expression of the object's type (such as createA()) I often use copy-init, probably just due to familiarity with the = form of assignment. Otherwise, when the initializer is a different type or there are multiple initializers for the object (e.g. multiple constructor arguments, or aggregate init) I prefer to use C++11 list-initialization (a.k.a uniform initialization syntax), which can be used in more places, e.g. to initialize aggregates as well as classes with user-defined constructors, and cannot be parsed as a function declaration:

A a1{ createA() };

The above form of list-init uses direct-init, whereas this form uses copy-init:

A a2 = { createA() };

When using list-init I prefer the direct-init form without the redundant = sign.

There are a few cases where list-init isn't possible, e.g. when a type has an initializer-list constructor (i.e. one taking a parameter that is a specialization of std::initializer_list) and you want to call a different constructor, but the initializer-list constructor would get chosen, e.g. std::vector<int> v{ 5u, 0 }; will not create a vector of five elements with value zero, but a vector with two elements with values five and zero

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • I am not sure the "brace-copy-initialization" is legal. [gcc 4.7.2 complains about it](http://ideone.com/BjXz2E). It's copy-initialization with an `initializer_list`: http://ideone.com/cM9zY4 – Arne Mertz Feb 25 '13 at 12:57
  • 1
    @ArneMertz, that's because your type is an aggregate, so it tries to use aggregate-init with the wrong number of members. The code in your own answer has the same problem for aggregates! For a non-aggregate it's legal. – Jonathan Wakely Feb 25 '13 at 13:02
  • Thanks, I see. One more reason not to use that form. – Arne Mertz Feb 25 '13 at 13:04
  • One issue with list-initialization is that it doesn't interact with auto well. `auto x{2};` doesn't make x an int! (It's an initiazilizer_list.) – Chris Hartman Feb 25 '13 at 18:42
  • @ChrisHartman, I don't consider that a serious issue. For trivial scalar types such as int it's not a problem, and for something like `auto foo{a, b, c};` it's the right choice because the context doesn't say what else it should be deduced as. As I said, I would prefer `auto x = 2;` any way for init from an expression of the right type – Jonathan Wakely Feb 25 '13 at 18:53
  • Agreed, I meant that merely as a warning that it might not do what some people expect. – Chris Hartman Feb 26 '13 at 00:36