C++ Braced Initialization

Posted by user on 05 Mar 2017

Since C++ 11 it's possible to use braces for construction and initialization. Although this is something you could ignore for the code you write, it's obviously important to know for the code you may read.

If you have a couple of years of experience in C++, the temptation can be great to keep your old habits because "All these new features are useless! The language is bloated! Those people in the committee!".

Let's make sense out of the bloat.

Braced initialization: an introduction

Braced initialization is when you write

std::string dat_string{"boom"};

instead of

std::string dat_string("boom");

When teaching C++ to someone for the first time, it has the immediate advantage of coherence as braced initialization can be used in more contexts:

int i{3}; // also legal: i(3) and i = 3

struct blah
{
int i{3}; // also legal: i = 3 but not i(3)
};

std::atomic<int> a{3}; // also legal: a(3) but not a = 3

For a detailed description and explanation of braced initialization, the item 7 of chapter 3 of Effective Modern C++ by Scott Meyers is an excellent read.

Braced initialization: a trap

Braced initialization is an initializers list with one element, thus, when writing:

std::vector<int> v{2, 1};

You are not writing:

// create a vector of two elements initialized to 1
// i.e. [1, 1]
std::vector<int> v(2, 1);

but this:

// create a vector containing two elements: 2 and 1
// i.e. [2, 1]
std::vector<int> v = {2, 1};

That's because vector accepts an initializers list has a construction parameter.

When writing generic code using templates, you must really think about your intent.

For example, the following code will behave differently if the V object accepts initializer lists as an argument:

template <typename V>
V my_factory(int i)
{
// mmm...
return V{i, i};
}

The morale of the story is that you should not blindly apply braced initialization everywhere.

Braced initialization: a review

Before we study braced initialization further, let's review the known advantages, heavily discussed in articles and books:

  • Works in most contexts;
  • No implicit copy construction (as opposed to =);
  • No more narrowing conversions: the compiler will emit an error in case of truncation instead of silently converting the value;
  • No more most vexing parse!
  • Just be careful with objects that accept an initializers list.

Is this enough?

If you look at the advantages above, it may look a bit weak. Sure, avoiding narrowing conversions is nice and the most vexing parse can be annoying when you face it, but is it worth it?

At quasardb we've switched to braced initialization by default for now more than one year. It has overall been a net positive.

Explicit intent

With braced initialization, it is easier to see when you are constructing an object or calling a function:

my_function(my_object{"value"});

Concise default construction

The habit of using braced initializers increases concision:

struct blah
{
// will use a default constructed string if none is provided
explicit blah(const std::string & s = {}) : _s{s} {}

// much shorter than
// explicit blah(const std::string & s = std::string()) : _s(s) {}

private:
std::string _s;
};

Flag the code as revised

When we adopt a new language feature, we obviously don't retroactively apply it to the whole base: that would be extremely time consuming.

However, every time we refactor, we update the near vicinity to our latest standards. When we see code without braced initialization, we know without looking at the git history that it hasn't been touched for a long while.

Out of the comfort zone

Adopting novelty is a way to communicate to the whole team that they should stay current (Continuous improvement is one of our core values).

When you integrate new language features, this forces you to be agile all the way down. You need a flexible continuous integration chain and everyone in the team has to keep an agile and open mind.

Street credits

When using braced initialization, you clearly state that you are an elite C++ programmer. Regularly breaking the build with unsupported code is bound to attract attention. Don't listen to the critics! They are all wrong.

Remember: if your code works on a compiler that is more than two months old, you are clearly a sub-par programmer.

Don't worry, if (when) you get fired, you can still apply at quasardb!

Topics: cpp, braces, initialization, Uncategorized