Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Foo({}) invoke Foo(0) instead of Foo()?

Executables produced by clang 3.5.0 and gcc 4.9.1 from the code

#include <iostream>

struct Foo
{
   Foo() { std::cout << "Foo()" << std::endl; }
   Foo(int x) { std::cout << "Foo(int = " << x << ")" << std::endl; }
   Foo(int x, int y) { std::cout << "Foo(int = " << x << ", int = " << y << ")" << std::endl; }
};

int main()                 // Output
{                          // ---------------------
   auto a = Foo();         // Foo()
   auto b = Foo(1);        // Foo(int = 1)
   auto c = Foo(2, 3);     // Foo(int = 2, int = 3)
   auto d = Foo{};         // Foo()
   auto e = Foo{1};        // Foo(int = 1)
   auto f = Foo{2, 3};     // Foo(int = 2, int = 3)
   auto g = Foo({});       // Foo(int = 0)          <<< Why?
   auto h = Foo({1});      // Foo(int = 1)
   auto i = Foo({2, 3});   // Foo(int = 2, int = 3)
}

behave as commented.

From cppreference: cpp/language/list initialization:

[...]

T( { arg1, arg2, ... } )    (7)

[...]

The effects of list initialization of an object of type T are:

If T is an aggregate type, aggregate initialization is performed.

Otherwise, If the braced-init-list is empty and T is a class type with a default constructor, value-initialization is performed.

[...]

I concluded that Foo({}) should call the default constructor.

Where's the bug?

like image 363
precarious Avatar asked Nov 06 '14 20:11

precarious


1 Answers

The default constructor is only applicable if you use one single pair of either braces:

auto a = Foo();         // Foo()
auto b = Foo{};         // Foo()

Foo({}) instead will only call constructors with the empty list as the argument, copy-list-initializing the parameter of whatever constructor is chosen. [dcl.init]/16:

If the destination type is a (possibly cv-qualified) class type:
— If the initialization is direct-initialization […] constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

You have one argument: The empty braced-init-list. There is a list-initialization sequence converting {} to int so the constructor Foo(int) is chosen by overload resolution. The parameter is initialized to zero as {} implies a value-intialization which, for scalars, implies a zero-initialization.

There is no bug in cppreferences documentation either: For (7) it is stated that

7) in a functional cast expression or other direct-initialization, with braced-init-list used as the constructor argument

Which clearly leads to the same result as with the above quote: The constructor is called with the (empty) braced-init-list.

like image 156
Columbo Avatar answered Oct 01 '22 12:10

Columbo