Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does double empty curly braces { { } } create a std::initializer_list<double> with one element, not zero?

I have the following constructor:

MyItem(std::initializer_list<double> l) {
    std::cout << "l size " << l.size() << ")" << std::endl;
}

Which is called later with double curly braces:

MyItem{{}}

The result l.size() gives is 1.

What's the mechanics behind such behavior?

It seems like nested {} plays like a default constructor for the only element, but I don't quite understand why and how type deduction works here.

like image 292
Nikolay Polivanov Avatar asked Jan 10 '16 16:01

Nikolay Polivanov


2 Answers

When you use braces (list-initialization) to initialize the MyItem object, the list constructor you have shown is very greedy.

These would pass an empty list:

MyItem foo({});
MyItem foo{std::initializer_list<double>{}};

This passes a list containing a single element - a value-initialized double (0.0):

MyItem foo{{}};

This works because there are certain contexts where you can simply use braces in place of a known type. Here, it knows from preferring the list constructor that the given list should contain double.

For completeness, this looks like it passes an empty list, but it actually value-initializes foo if it has a default constructor (or in special cases, does something almost equivalent). If there's no default constructor, it would choose the list constructor, as shown here.

MyItem foo{};
like image 142
chris Avatar answered Nov 18 '22 09:11

chris


This expression

MyItem{{}}

denotes explicit type conversion (the functional notation).

According to the C++ Standard (5.2.3 Explicit type conversion (functional notation))

  1. Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.

Class MyItem has conversion initializer-list constructor

MyItem(std::initializer_list<double> l) {
    std::cout << "l size " << l.size() << ")" << std::endl;
}

that is selected for the explicit type conversion. In fact it is equivalent to the call

MyItem( {{}} );

So the constructor gets an initializer list with one element

{ {} }

A scalar object of type double can be initialized with an empty braces {}.

As result the expression creates a temporary object of type MyItem which is initialized by an initializer list that contains one element of type double that is value-initialized by means of empty braces.

like image 22
Vlad from Moscow Avatar answered Nov 18 '22 09:11

Vlad from Moscow