60

What difference between these ways of initializing object member variables in C++11 ? Is there another way ? which way is better (performance) ?:

class any {
  public:
    obj s = obj("value");
    any(){}
};

Or

class any {
  public:
    obj s;
    any(): s("value"){}
};

Thanks.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Ahmed T. Ali
  • 1,021
  • 1
  • 13
  • 22
  • I agree, they are the same. I just wanted to add that for further reference you can read "The C++ Programming Language 4th Edition" section 17.4.4. – mhcuervo Dec 08 '14 at 06:11
  • Very similar to [Has the new C++11 member initialization feature at declaration made initialization lists obsolete?](http://stackoverflow.com/q/24149924/1708801) – Shafik Yaghmour Dec 08 '14 at 10:22

3 Answers3

61

No, these are not the same.

The difference between them is the same that applies for direct-initialization vs. copy-initialization, which is subtle but often very confusing.

§12.6.2 [class.base.init]:

  1. The expression-list or braced-init-list in a mem-initializer is used to initialize the designated subobject (or, in the case of a delegating constructor, the complete class object) according to the initialization rules of 8.5 for direct-initialization. [...]

  2. In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then

    — if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;

§8.5 [dcl.init]:

  1. The initialization that occurs in the form

    T x = a;

as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization.

Initializing a non-static data member on a member-initializer-list follows the rules of direct-initialization, which doesn't create intermediate temporaries that need to be moved/copied (if compiled without a copy-elision), neither the type of the data member must be copyable/movable (even if the copy is elided). In addition, a direct-initialization introduces an explicit context, while a copy-initialization is non-explicit (if a constructor selected for the initialization is explicit, the program won't compile).

In other words, the obj s = obj("value"); syntax won't compile if obj is declared as:

struct obj
{
    obj(std::string) {}
    obj(const obj&) = delete;
};

or:

struct obj
{
    obj(std::string) {}
    explicit obj(const obj&) {}
};

As a more tangible example, while the below won't compile:

struct any
{
   std::atomic<int> a = std::atomic<int>(1); // ill-formed: non-copyable/non-movable
   std::atomic<int> b = 2; // ill-formed: explicit constructor selected
};

this one will:

struct any
{
    std::atomic<int> a;
    std::atomic<int> b{ 2 };
    any() : a(1) {}
};

Which way is better (performance) ?

With a copy-elision enabled both have identical performance. With copy-elision disabled, there is an additional copy/move constructor call upon every instantiation when the copy-initialization syntax is used (that obj s = obj("value"); is one of).


Is there another way ?

The brace-or-equal-initializer syntax allows one to perform a direct-list-initialization as well:

class any {
public:
    obj s{ "value" };
    any() {}
};

Are there any other differences?

Some other differences that are worth mentioning are:

  1. Brace-or-equal-initializer must reside in a header file along with a class declaration.
  2. If both are combined, member-initializer-list takes priority over brace-or-equal-initializer (that is, brace-or-equal-initializer is ignored).
  3. (C++11 only, until C++14) A class that uses brace-or-equal-initializer violates constraints for an aggregate type.
  4. With the brace-or-equal-initializer syntax it's not possible to perform a direct-initialization other than a direct-list-initialization.
Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • 1
    Thanks , it seems you are right :) , I have another question , if I say : obj s{"value"} , it will store in stack ,right ? and when I say : obj *s = new s("value") , it will store in the heap ?? if so , when I need to store in either stack or heap ? can you explain it plz – Ahmed T. Ali Dec 08 '14 at 09:47
  • @AhmedTajElsir if the object that contains (as a data member) `obj s{"value"}` is allocated on the heap, then `s` will be allocated on the heap as well. Whether you should allocate data members on stack/heap depends on design decisions/further usage of those objects – Piotr Skotnicki Dec 08 '14 at 10:02
  • can you clarify "...depends on design decisions/further usage of those objects" . – Ahmed T. Ali Dec 08 '14 at 10:23
  • 1
    @AhmedTajElsir it's too broad subject to write it in a comment, you might find the answer to [How do I choose heap allocation vs. stack allocation in C++?](http://stackoverflow.com/questions/7973138/how-do-i-choose-heap-allocation-vs-stack-allocation-in-c) question very useful, as it's exactly what you ask about – Piotr Skotnicki Dec 08 '14 at 10:26
  • Could anybody explain what is exactly meant by "violates constraints for an aggregate type" - pont 3. at the end of the article, thanks! – Dom Nov 07 '16 at 15:34
  • @PiotrSkotnicki , please have a look at the following question: https://stackoverflow.com/questions/46634869/is-it-direct-initialization-or-copy-initialization . I would really appreciate your help :-) – K.Mulier Oct 08 '17 at 18:56
7

Both examples are equivalent.
Though only if the type is copyable or movable (check it for yourself) and NRVO is actually done (any halfway decent compiler will do it as a matter of course).

Though if you had many constructors and constructor-chaining were inappropriate, the first method would allow you not to repeat yourself.

Also, you can use that method to define aggregates with defaults different from aggregate-initialization for (some) members since C++14.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
5

They are the same.

Neither is better than the other in terms of performance, and there is no other way to initialise them.

The benefit of in-class initialisation (the first in your example) is that the order of initialisation is implicit. In initialiser list you have to explicitly state the order - and compilers will warn of out-of-order initialisation if you get the ordering incorrect.

From the standard:

12.6.2.5
nonstatic data members shall be initialized in the order they were declared 
in the class definition

If you get the order wrong in your list, GCC will complain:

main.cpp: In constructor 'C::C()':
main.cpp:51:9: warning: 'C::b' will be initialized after
main.cpp:51:6: warning:   'int C::a'

The benefit of initialiser lists is perhaps a matter of taste - the list is explicit, typically in the source file. In-class is implicit (arguably), and is typically in the header file.

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • No need to state the order in ctor-init-lists, it is always implicit and independent of how you order the initializers. – Deduplicator Dec 08 '14 at 05:53
  • 2
    Thanks a lot , personally I like first method , because it's familiar (Java programer before :D ) , but I was afraid there is performance difference . – Ahmed T. Ali Dec 08 '14 at 05:58