Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Style for the initialization of single variables [closed]

There are a couple of differing styles when initializing automatically managed simple variables other than via assignment. I was wondering if there are any specific reasons to favour one over the other or is it just a matter of style.

Using parentheses is appealing because it feels similar to instantiating an object

double answer(42.0);
ComplexNumber i(0,1);

while using braces is appealing because it feels similar to initialising a container

double answer{42};
std::vector<double> i{0,1};
double i2[] = {0,1};

Is there any particular reason to favour one style over the other?

like image 268
DuncanACoulter Avatar asked Jun 14 '13 16:06

DuncanACoulter


2 Answers

Look here: GotW #1 : Variable Initialization. It's a detailed description of answer from H. Sutter.

H. Sutter talks above in a general sense of old and new styles of Variable Initialization.

Below is showed a quick synopsis from the article, according to your start-topic context.


This pair

double answer(42.0); // (1)
double answer{42};  // (2)

in fact, is similar to the next initialization example:

widget w(x);                // (d)
widget w{x};                // (e)

These are both direct initialization. However, note that the syntax {x} creates an initializer_list. If widget has a constructor that takes an initializer_list, that constructor is preferred; otherwise, if widget has a constructor that takes whatever type x is (possibly with conversions), that constructor is used.

There are two major differences that make (2, e) superior to (1, d):

  • First, syntax (2, e) is unambiguous and avoids the "vexing parse". If x is a type name, then (1, d) is a function declaration even if there is also a variable named x in scope (see above), whereas (2, e) is never a function declaration.
  • Second, syntax (2, e) is safer because it does not allow narrowing (a.k.a. “lossy”) conversions that are otherwise allowed for some built-in types. Consider:

    int i1( 12.345 ); // ok: toss .345, we didn't like it anyway

    int i2{ 12.345 }; // error: would be lossy implicit narrowing

Next pair

ComplexNumber i(0,1); // (3)
std::vector<double> i{0,1}; // (4)

is tied with initialization of complex objects. Both looks quite the same, but the second one helps us to avoid "vexing parse", like:

ComplexNumber       w( real(), img() );       // oops, vexing parse 

Besides of that, this way make code more clear (if we use initializer_list, it's more clear that's initialization), and, moreover, alleviate syntax in some cases, for instance:

draw_rect({ origin, selection });                  // C++11

Sutter guildeline is: prefer to use initialization with { }, such as vector<int> v = { 1, 2, 3, 4 }; or auto v = vector<int>{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine.

We can use ()-initialization for the explicit call of the special constructor.

like image 147
Boris Avatar answered Oct 29 '22 19:10

Boris


Adding to Boris's answer there's another pitfall to avoid that's not mentioned in Herb's blog post.

The following code is illegal:

#include <initializer_list>

int main() {
  auto i{42}; // not the same as 'auto i(42);'
  ++i;
}

The error message raised by gcc 4.7.2 is:

error: no match for 'operator++' in '++i'

The reason is that auto deduces the type of {42} to be std::inilializer_list<int> rather than int as many could believe. There's no operator ++ for std::initializer_list<int> and hence the error. If you replace {42} with (42) then the code compiles fine.

like image 40
Cassio Neri Avatar answered Oct 29 '22 20:10

Cassio Neri