3

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)

  • 1
    Thank you for considering the on-topic page carefully when posting your excellent question. However, it seems that you are asking about the best practices around some very nuanced details of C++. As this is more about using the language correctly than about software engineering concepts, I think the question might be better suited for Stack Overflow – I'm voting to move the question there. – amon Jul 1 '17 at 13:39
  • @amon Thanks for your feedback and advice. If you believe the question is better suited for Stack Overflow, moving it there is likely a good decision, I see now that these kind of detailed questions doesn't fit the more general concepts that are discussed here at SE Software Engineering. (Q1 is very suited for SO, whereas, based on my experience, the question might possibly lead to a quick closing as "primarily opinion-based" due to Q2, but I'll have to take my chances!). I guess you need a few more votes from high-rep users (or moderator intervention) to execute the migration? – dfri Jul 1 '17 at 14:22
4

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.