Benefits of Initialization lists
The second version is calling string's default ctor and then string's copy-assignment operator -- there could definitely be (minor) efficiency losses compared to the first one, which directly calls c's copy-ctor (e.g., depending on string's implementation, there might be useless allocation-then-release of some tiny structure). Why not just always use the right way?-)
Advantages of using initializer list?
If you don't use the initializer list, the member or base class gets default constructed before the opening curly brace.
So, your calls to set it later will add an operator=()
call.
If you use the initializer list, the member or base class has the proper constructor called.
Depending on your classes, this might be required or faster.
Why should I prefer to use member initialization lists?
For POD class members, it makes no difference, it's just a matter of style. For class members which are classes, then it avoids an unnecessary call to a default constructor. Consider:
class A
{
public:
A() { x = 0; }
A(int x_) { x = x_; }
int x;
};
class B
{
public:
B()
{
a.x = 3;
}
private:
A a;
};
In this case, the constructor for B
will call the default constructor for A
, and then initialize a.x
to 3. A better way would be for B
's constructor to directly call A
's constructor in the initializer list:
B()
: a(3)
{
}
This would only call A
's A(int)
constructor and not its default constructor. In this example, the difference is negligible, but imagine if you will that A
's default constructor did more, such as allocating memory or opening files. You wouldn't want to do that unnecessarily.
Furthermore, if a class doesn't have a default constructor, or you have a const
member variable, you must use an initializer list:
class A
{
public:
A(int x_) { x = x_; }
int x;
};
class B
{
public:
B() : a(3), y(2) // 'a' and 'y' MUST be initialized in an initializer list;
{ // it is an error not to do so
}
private:
A a;
const int y;
};
Advantages of list initialization in C++11
A significant advantage you don't mention is its usefulness in template metaprogramming, where you can now compute something using templates, and then, in a constexpr function, expand some template data structure and store the results in an array.
See for instance here: Populate An Array Using Constexpr at Compile-time
In the code:
template<unsigned... Is>
constexpr Table MagicFunction(seq<Is...>){
return {{ whichCategory(Is)... }};
}
I don't think there was any way to do anything like that prior to C++11.
What are the advantages of list initialization (using curly braces)?
Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
List initialization does not allow narrowing (§iso.8.5.4). That is:
- An integer cannot be converted to another integer that cannot hold its value. For example, char
to int is allowed, but not int to char. - A floating-point value cannot be converted to another floating-point type that cannot hold its
value. For example, float to double is allowed, but not double to float. - A floating-point value cannot be converted to an integer type.
- An integer value cannot be converted to a floating-point type.
Example:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
The only situation where = is preferred over {} is when using auto
keyword to get the type determined by the initializer.
Example:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
Prefer {} initialization over alternatives unless you have a strong reason not to.
Are member-initialization lists really more efficient?
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.
Advantage using an aggregate initialization list over a constructor?
I don't know about performance advantages, but in general using the constructor is preferred.
This is because with A, members a,b,c,d can be made private. Thus, you get encapsulation with your A approach, which you don't have in B.
As a class designer, you can enforce strict usage and assignment of member variables via a constructor. In your B, class scenario, you can't.
So while you may get a small boost in perf, for using B, I would wager it to be negligible, and would be negated by the potential headache of having unprotected class members.
Why use member init lists if there is default member initialization
There is no "must" here, a decision would probably be made based on circumstances.
For example, if the initialized value is not a constant, or if the value must be different in each of multiple constructors it makes sense to use the initializer list.
If you have to support pre-C++11 (and this may be more common than you think on large projects) you must always use the initializer list. Corresondingly if most of your code maintainers are not familiar with C++11 features it may be better to use the longstanding mechanism rather than the inline initialization.
If using C++11 is not an issue and you're initializing to a single constant value across multiple constructors, than the initializer form probably makes more sense.
Related Topics
How to Overload Array Index Operator for Wrapper Class of 2D Array
Visual Studio 2015 Code Analysis C6386 Warns of Buffer Overrun
How to Check If a File Is Gzip Compressed
How to Write Video File in Opencv 2.4.3
What Is the Array Form of 'Delete'
Inlining Template Specialization
C++ Template Function Default Value
Calling Erase with Iterator VS Const_Iterator
Why Does Adding 0 to the End of Float Literal Change How It Rounds (Possible Gcc Bug)
What Are the Advantages and Disadvantages of Implementing Classes in Header Files
How Does Sizeof Know the Size of the Operand Array
How to Know Underlying Type of Class Enum
How to Implement an Function Equivalent to Bwmorph Matlab Function in Opencv