Search code examples
c++performanceconstructorassignment-operatormember-initialization

Are member-initialization lists really more efficient?


I agree with the consensus that it's generally best to initialise C++ data members in a member initialization list rather than the body of a constructor, but I am sceptical of this explanation

The other (inefficient) way to build constructors is via assignment, such as: Fred::Fred() { x_ = whatever; }. In this case the expression whatever causes a separate, temporary object to be created, and this temporary object is passed into the x_ object’s assignment operator. Then that temporary object is destructed at the ;. That’s inefficient.

Is this actually correct? I would have expected the compiler to elide the default-constructed temporary object which is immediately replaced by assignment in the body. I don't know why I expected this but having read the above claim I guess I have been quietly assuming it for years.

Are member initialization lists actually more efficient? If so, is it for this reason?


Solution

  • Using member init list,

    #include <string>
    
    struct Fred {
      Fred() : x_("hello") { }
      std::string x_;
    };
    
    int main() {
      Fred fred;
    }
    

    Clang 3.9.1 and gcc 6.3 generate the following with -O3 -fno-exceptions (Compiler Explorer):

    main:                                   # @main
            xor     eax, eax
            ret
    

    If we do an assignment in the body instead:

    #include <string>
    
    struct Fred {
      Fred() { x_ = "hello"; }
      std::string x_;
    };
    
    int main() {
      Fred fred;
    }
    

    both generate a lot more code, e.g. Clang 3.9.1 outputs this:

    main:                                   # @main
            push    rbx
            sub     rsp, 32
            lea     rbx, [rsp + 16]
            mov     qword ptr [rsp], rbx
            mov     qword ptr [rsp + 8], 0
            mov     byte ptr [rsp + 16], 0
            lea     rdi, [rsp]
            xor     esi, esi
            xor     edx, edx
            mov     ecx, .L.str
            mov     r8d, 5
            call    std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_replace(unsigned long, unsigned long, char const*, unsigned long)
            mov     rdi, qword ptr [rsp]
            cmp     rdi, rbx
            je      .LBB0_2
            call    operator delete(void*)
    .LBB0_2:
            xor     eax, eax
            add     rsp, 32
            pop     rbx
            ret
    
    .L.str:
            .asciz  "hello"
    

    So it seems member init lists really are more efficient, at least for some cases, even with modern compilers.