Tuesday coding tips are super short posts about various tidbits mainly from C++, but also from other programming languages I use. You can also follow the #TuesdayCodingTips hashtag on Mastodon and Linkedin.
There is a five-hundred-page book about C++ initialization out there. Why? Because we cater to way too many styles and many constructor capabilities. This tip is not about explaining all the options but rather about some good practices you can use to minimize mistakes.
The “classic” or pre-C++11 way of initializing objects has the pitfall of triggering the “most vexing parse”. In such case, the compiler cannot tell whether you are calling a constructor or making a function declaration. And because C allowed for function declaration inside functions, it will pick the function declaration. You can omit the braces for the default constructor, but you can still encounter problems as shown in the ‘mvp2’ case. Trunk versions of GCC and Clang emit a helpful warning in this case.
// Classic C++ style
Object obj1(param1, param2); // This works
Object mvp1(); // Most Vexing Parse
Object mvp2(int(param1), int(param2)); // Also MVP
The “modern”, “universal” or post-C++11 way is also not the best one. It is consistent with struct initialization but can invoke incorrect constructors if you intermix normal parameters and std::initializer_list as demonstrated with the std::vector.
// Modern (uniform) C++ style
Object obj2{param1, param2};
Object obj2_2 = {param1, param2};
std::vector<int> vec(10); // vector containing ten default constructed items
std::vector<int> vec{10}; // vector containing one element with value 10
Then you have what I call a “Java” style. It’s verbose and has the same issues if you use curly braces. At least it doesn’t trigger MVP. The last version is what I often use due to its similarity to many other languages where you use var or let to denote the variables and usually specify the type name after the variable name.
// Verbose Java style
Object obj3 = Object(param1, param2);
Object obj3_2 = Object{param1, param2};
// Modern language style
auto obj4 = Object(param1, param2);
auto obj4_2 = Object{param1, param2};
Note that even though there is an assignment operator, no assignment, copy or move is happening — objects are constructed directly.
I recommend using the “modern language style” with simple braces only for regular constructors and curly braces only for initializer-list constructors. It makes your intent clear and prevents you from accidentally shooting yourself in the foot. “Java style” might be preferable for class attribute declarations, with the same rules regarding braces.
More on the subject: