Update. I made an error in the original text: empty braces are sometimes treated as a zero-size initializer list.

Some time ago, I showed how boost::value_initialized can be used to value-initialize objects in generic components in C++03. In C++11 it is practically not necessary. C++11 gives you a way to force value-initialization of your objects in any possible initialization context.

Value initialization

Are you familiar with term “value initialization”? It is sometimes confused with “default-initialization”. In the following declaration:

struct widget { int x; int y; std::string title; }; void default_initialization() { int i; // default-initialize i std::mutex m; // default-initialize m widget w; // default-initialize w int a[9]; // default-initialize a }

i is default-initialized. Its value is indeterminate (no initialization is performed). This is so for performance reasons, because sometimes we do not need an initial value. For instance:

int i; // uninitialized std::cin >> i; // read initial value

Let’s continue with our example: m is also default initialized. But because std::mutex provides a default constructor, this constructor is chosen for default-initialization.

For w , because type widget does not provide a custom default constructor, an implicit default constructor is generated (and called here) by the compiler: it calls default constructors for member sub-objects, which perform no initialization for x and y (as they are int s) and calls user-provided default constructor for title . For a , each element of the array is default-initialized (left in indeterminate state).

As we can see, for built-in types, default-initialization leaves the objects in indeterminate state. Value-initialization enables us to tell that for built-in types, objects should be initialized with value 0. We were able to use it in C++03 in certain contexts:

struct value_initialization { int i; std::mutex m; widget w; int a[9]; value_initialization() : i() // value-initialize i , m() // value-initialize m , w() // value-initialize w , a() // value initialize a };

In initialization list of the constructor () means “value-initialize”. That is: initialize i with value 0. For m , call its default constructor. For w , its members x and y are initialized with value 0, and for title we call string ’s default constructor. Integers in a are initialized with value 0.

If we wanted to trigger such value initialization for automatic objects in C++03, it is possible, but we have to select the appropriate initialization syntax for each object:

void manual_value_initialization() { int i = 0; // "=0" for scalars std::mutex m; // nothing here widget w = {}; // aggregate initialization int a[9] = {}; // aggregate initialization }

But now, what do you do if you are in a function template:

template <typename T> void manual_value_initialization() { T t = /* TYPE WHAT ? */; }

Since T can be int , std::mutex , widget or int[9] , which syntax do you pick for value-initialization? None will work for all four types.

Value-initialization syntax

In C++11 the last problem is solved with the “brace” syntax:

template <typename T> void cpp11_value_initialization() { T t {}; // value-initialize }

{} just means “value-initialize”, but unlike () , it works in any initialization context.

Going back to our example with class called value_initialization , it is risky to put built-in types as members in a class, because you may forget to call their value initialization in the constructor(s). Especially, when you add a new member, and the body of the constructor is in another “cpp” file, you may forget to add value-initialization in all constructors. In C++11, you can fix that problem:

struct cpp11_value_initialization { int i {}; std::mutex m {}; widget w {}; int a[9] {}; // no default constructor required };

Syntax int i {}; here means, “unless specified otherwise in the constructor, value-initialize member i upon initialization.” Similarly, in “generic context”:

template <typename T> class C { T member {}; // ... };

Note that if a class defines (implicitly or not) a default constructor, empty braces are not interpreted as zero-element initializer list (see this post for more details).

This is why the braced initialization syntax is called “uniform initialization”: because it works the same in different (all) initialization contexts: in constructor initialization list, for declaring automatic, temporary, global, heap-allocated variables, for class members. Note that if we used () to “initialize” member in the last example, we would be in fact declaring a member function. This is why initialization with () is not uniform.

“Uniform” does not mean that () and {} will do the same thing. For an example, see this post.

The empty brace syntax enables a funny-looking way of creating tags. A tag is an empty structure that does not store any value but whose type is used for selecting overloaded constructors of functions. You can find them in the Standard Library, for instance std::allocator_arg , std::nothrow , std::piecewise_construct , std::defer_lock . Such tags can be declared as one-liners:

constexpr struct allocator_arg_t{} allocator_arg{};

The first empty braces indicate that class allocator_arg_t has empty definition; the second indicates that we are value-initializing a global constant.