Are member initialization lists really more efficient?

I agree with the consensus that, in general, it is best to initialize C ++ data members in the member initialization list and not in the constructor body, but I am skeptical about this explanation

Another (inefficient) way to construct constructors is through assignment, for example: Fred::Fred() { x_ = whatever; }. Fred::Fred() { x_ = whatever; }. In this case, the expression calling the separate temporary object, and this temporary object is passed to the x_ object assignment operator. Then this temporary object is destroyed by ; . It is inefficient.

Is this really right? I would expect the compiler to return to the default temporary object, which is immediately replaced by the destination in the body. I do not know why I expected this, but after reading the above statement, I think that I calmly accepted it for many years.

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

+7
c ++ performance assignment-operator constructor
source share
3 answers

Using the element initialization 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 using -O3 -fno-exceptions ( Compiler Explorer ):

 main: # @main xor eax, eax ret 

If we perform an assignment in the body:

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

both generate a lot more code, for example. Clang 3.9.1 displays 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 that init member lists are indeed more efficient, at least for some cases, even with modern compilers.

+7
source share

In the words of Alexandrescu and Sutter (element 9) Do not pessimize prematurely

Avoiding premature optimization does not mean hopelessly hurt efficiency. By premature pessimization, we mean to write such gratuitous potential inefficiencies as:

• Determining the parameters of the passage according to the parameters when passing through the link is suitable. (See paragraph 25.)

• Using the + + postfix when the prefix version is just as good. (See Paragraph 28.)

• Using assignment inside constructors instead of initializer list. (See paragraph 48.)

Whenever you write assignments inside constructors, your code reviewers will be in warning mode: is something special happening? Does he really need a special two-stage initialization (because in any case the implicit construction of the member is created, which is created!). Do not surprise readers of your code for free.

Please note that Alexandrescu and Sutter continue in section 48 to discuss potential inefficiencies, but do not state anywhere that there is real inefficiency in real optimized code. This also does not apply to the expression of intent and avoiding the risk of inefficiency.

+8
source share

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

Actually, yes. When a member is initialized, you pass the value directly to the constructor, otherwise an object constructed by default will be created and the assignment operator will be called. Please note that this is not a "temporary" mention in the above quote, it is about the field itself.

You can see it live here

 class Verbose { public: Verbose() { std::cout << "Verbose::Verbose()" << std::endl; } Verbose( int ) { std::cout << "Verbose::Verbose(int)" << std::endl; } Verbose &operator=( int ) { std::cout << "Verbose::operator=(int)" << std::endl; } }; class A { public: A() : v( 0 ) {} A(int) { v = 0; } private: Verbose v; }; int main() { std::cout << "case 1 --------------------" << std::endl; A a1; std::cout << "case 2 --------------------" << std::endl; A a2( 0 ); // your code goes here return 0; } 

exit:

 case 1 -------------------- Verbose::Verbose(int) case 2 -------------------- Verbose::Verbose() Verbose::operator=(int) 
+3
source share

All Articles