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)