Advantages of Using Initializer List

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



Leave a reply



Submit