C++11 member initializer list vs in-class initializer?

前端 未结 3 1736
走了就别回头了
走了就别回头了 2020-11-30 19:48

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

class any          


        
相关标签:
3条回答
  • 2020-11-30 20:26

    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.
    0 讨论(0)
  • 2020-11-30 20:37

    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.

    0 讨论(0)
  • 2020-11-30 20:49

    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.

    0 讨论(0)
提交回复
热议问题