Ray
Ray

Reputation: 8573

Properly initialising variables in modern C++ (C++11 and above), using () or {}?

The C++ reference pages say that () is for value initialisation, {} is for value and aggregate and list initialisation. So, if I just want value initialisation, which one do I use? () or {}? I'm asking because in the book "A Tour of C++" by Bjarne himself, he seems to prefer using {}, even for value initialisation (see for example pages 6 and 7), and so I thought it was good practice to always use {}, even for value initialisation. However, I've been badly bitten by the following bug recently. Consider the following code.

auto p = std::make_shared<int>(3);
auto q{ p };
auto r(p);

Now according to the compiler (Visual Studio 2013), q has type std::initializer_list<std::shared_ptr<int>>, which is not what I intended. What I actually intended for q is actually what r is, which is std::shared_ptr<int>. So in this case, I should not use {} for value initialisation, but use (). Given this, why does Bjarne in his book still seem to prefer to use {} for value initialisation? For example, he uses double d2{2.3} at the bottom of page 6.

To definitively answer my questions, when should I use () and when should I use {}? And is it a matter of syntax correctness or a matter of good programming practice?

Oh and uh, plain English if possible please.

EDIT: It seems that I've slightly misunderstood value initialisation (see answers below). However the questions above still stands by and large.

Upvotes: 44

Views: 7308

Answers (7)

Paul Jurczak
Paul Jurczak

Reputation: 8165

Scott Mayers just posted a relevant blog entry Thoughts on the Vagaries of C++ Initialization. It seems that C++ still has a way to go before achieving a truly uniform initialization syntax.

Upvotes: 0

einpoklum
einpoklum

Reputation: 131544

Herb Sutter seems to be making an argument in CppCon 2014 (39:25 into the talk) for using auto and brace initializers, like so:

auto x = MyType { initializers };

whenever you want to coerce the type, for left-to-right consistency in definitions:

  • Type-deduced: auto x = getSomething()
  • Type-coerced: auto x = MyType { blah }
  • User-defined literals auto x = "Hello, world."s
  • Function declaration: auto f { some; commands; } -> MyType
  • Named Lambda: using auto f = [=]( { some; commands; } -> MyType
  • C++11-style typedef: using AnotherType = SomeTemplate<MyTemplateArg>

Upvotes: 1

Jean-Baptiste Yun&#232;s
Jean-Baptiste Yun&#232;s

Reputation: 36401

{} is value initialization if empty, if not it is list/aggregate initialization.

From the draft, 7.1.6.4 auto specifier, 7/... Example,

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>

Rules are a little bit complex to explain here (even hard to read from the source!).

Upvotes: 2

user1978011
user1978011

Reputation: 3589

There is another important difference: The brace initializer requires that the given type can actually hold the given value. In other words, it forbids narrowing of the value, like rounding or truncation.

int a(2.3); // ok? a will hold the value 2, no error, maybe compiler warning
uint8_t c(256); // ok? the compiler should warn about something fishy going on

As compared to the brace initialization

int A{2.3}; // compiler error, because int can NOT hold a floating point value
double B{2.3}; // ok, double can hold this value
uint8_t C{256}; // compiler error, because 8bit is not wide enough for this number

Especially in generic programming with templates you should therefore use brace initialization to avoid nasty surprises when the underlying type does something unexpected to your input values.

Upvotes: 6

Galik
Galik

Reputation: 48615

Scott Meyers has a fair amount to say about the difference between the two methods of initialization in his book Effective Modern C++.

He summarizes both approaches like this:

Most developers end up choosing one kind of delimiter as a default, using the other only when they have to. Braces-by-default folks are attracted by their unrivaled breadth of applicability, their prohibition of narrowing conversions, and their immunity to C++’s most vexing parse. Such folks understand that in some cases (e.g., creation of a std::vector with a given size and initial element value), parentheses are required. On the other hand, the go-parentheses-go crowd embraces parentheses as their default argument delimiter. They’re attracted to its consistency with the C++98 syntactic tradition, its avoidance of the auto-deduced-a-std::initializer_list problem, and the knowledge that their object creation calls won’t be inadvertently waylaid by std::initializer_list constructors. They concede that sometimes only braces will do (e.g., when creating a container with particular values). There’s no consensus that either approach is better than the other, so my advice is to pick one and apply it consistently.

Upvotes: 30

R Sahu
R Sahu

Reputation: 206597

This is my opinion.

When using auto as type specifier, it's cleaner to use:

auto q = p;   // Type of q is same as type of p
auto r = {p}; // Type of r is std::initializer_list<...>

When using explicit type specifier, it's better to use {} instead of ().

int a{};   // Value initialized to 0
int b();   // Declares a function (the most vexing parse)

One could use

int a = 0; // Value initialized to 0

However, the form

int a{};

can be used to value initialize objects of user defined types too. E.g.

struct Foo
{
   int a;
   double b;
};


Foo f1 = 0;  // Not allowed.
Foo f1{};    // Zero initialized.

Upvotes: 24

First off, there seems to a terminology mixup. What you have is not value initialisation. Value initialisation happens when you do not provide any explicit initialisation arguments. int x; uses default initialisation, the value of x will be unspecified. int x{}; uses value initialisation, x will be 0. int x(); declares a function—that's why {} is preferred for value initialisation.

The code you've shown does not use value initialisation. With auto, the safest thing is to use copy initialisation:

auto q = p;

Upvotes: 7

Related Questions