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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With