List initialization by empty braced-init-list (value-initialization or by initializer_list) vs explicit default initialization, for “list” types

softwareengineering.stackexchange https://softwareengineering.stackexchange.com/questions/351990

  •  15-01-2021
  •  | 
  •  

Question

Background

Based on Item 4 in Scott Mayers Effective C++, when appropriate, it can be good practice to initialize all member properties of custom types via the member initialization list (in same order as their declarations), even for non-POD types with accessible default CTOR:s.

[Excerpt From: Scott Meyers. “Effective C++ Third Edition 55 Specific Ways to Improve Your Programs and Designs.”]

Item 4: Make sure that objects are initialized before they're used

...

For objects of built-in type ..., there is no difference in cost between initialization and assignment, but for consistency, it's often best to initialize everything via member initialization. Similarly, you can use the member initialization list even when you want to default-construct a data member; just specify nothing as an initialization argument.”

... but having a policy of always listing every data member on the initialization list avoids having to remember which data members may go uninitialized if they are omitted.

Now, for member variables which have types that have CTOR:s accepting an std::initializer_list parameter, and where a "list" has a descriptive value itself w.r.t. the type (e.g. for std::vector), even for default construction it could be valuable and descriptive to use an empty braced-init-list in the explicit initialization of the variable in the the member initializer list of the owning type.

Consider e.g. a custom type with a std::vector member that is to be initialized as empty. For such situations, I've seen the following three variations (in the context of explicitly using the member initialization list for the std::vector member):

// A) Using std::vector:s default CTOR.
struct FooA {
  FooA() : numbers() {};
private:
  std::vector<int> numbers;
};

// B) Using an empty braced-init-list invoking value-initialization;
//    also using std::vector:s default CTOR.
struct FooB {
  FooB() : numbers{} {};
private:
  std::vector<int> numbers;
};

// C) Empty braced-init-list used to explicitly invoke std::vector:s
//    std::initialization_list CTOR.
struct FooC {
  FooC() : numbers({}) {};
private:
  std::vector<int> numbers;
};

Where the "descriptive value" of using an empty braced-init-list could be apparent when comparing to the non-empty case:

// D) Non-empty braced-init-list use to explicitly invoke std::vector:s
//    std::initialization_list CTOR.
struct FooD {
  FooD() : numbers({1, 2, 3}) {};
  /* or: 
  FooD() : numbers{1, 2, 3} {}; */
private:
  std::vector<int> numbers;
};

Questions

  • [Q1] When comparing A & B vs C above: could the latter be non-preferred w.r.t. some overhead when explicitly calling the std::initializer_list CTOR with an empty list? (As compared to simply using the default CTOR directly).

  • [Q2] W.r.t. best practices/showing intent: could using an empty braced-init-list (B & C) be preferred over an explicit default CTOR call (A) in cases where a "list" has a direct descriptive value to the type which it initializes (e.g. such as std::vector)? Or should we rather prefer to be explicit about the call to the default CTOR (A and possible B), not putting to much weight into describing the "list association" of the type?


(This is my first post at SE-Software Engineering; hopefully it is somewhat on-topic here, as I deemed in inappropriate for StackOverflow (mostly due to Q2 above); please advice me if otherwise)

Was it helpful?

Solution

You're effectively talking about the distinction between "value initialization" (T{}) and "here's a list of values that happens to be empty" (T({})). I would say that best practices should be to do the thing that best describes your intent.

There is a difference between value initializing a vector and initializing one from a list that just so happens to be empty. In the latter case, you may someday add one or more elements to that list. In the former case, you never will. So you need to do what best describes the meaning of your code.

As for the question of efficiency, it almost certainly does not matter. You should start from the perspective of code clarity; if performance becomes an issue, you can adjust it later. The choice between "empty list" and "value initialization" is first and foremost about communicating your intent.

Licensed under: CC-BY-SA with attribution
scroll top