Based on this code
struct Foo
{
Foo()
{
cout << "default ctor" << endl;
}
Foo(std::initializer_list<Foo> ilist)
{
cout << "initializer list" << endl;
}
Foo(const Foo& copy)
{
cout << "copy ctor" << endl;
}
};
int main()
{
Foo a;
Foo b(a);
// This calls the copy constructor again!
//Shouldn't this call the initializer_list constructor?
Foo c{b};
_getch();
return 0;
}
The output is:
default ctor
copy ctor
copy ctor
In the third case, I'm putting b into the brace-initialization which should call the initializer_list<> constructor.
Instead, the copy constructor takes the lead.
Will someone of you tell me how this works and why?
As pointed out by Nicol Bolas, the original version of this answer was incorrect: cppreference at the time of writing incorrectly documented the order in which constructors were considered in list-initialization. Below is an answer using the rules as they exist in the n4140 draft of the standard, which is very close to the official C++14 standard.
The text of the original answer is still included, for the record.
Updated Answer
Per NathanOliver's comment, gcc and clang produce different outputs in this situation:
g++ -std=c++14 -Wall -pedantic -pthread main.cpp && ./a.out
default ctor
copy ctor
copy ctor
initializer list
clang++ -std=c++14 -Wall -pedantic -pthread main.cpp && ./a.out
default ctor
copy ctor
copy ctor
gcc is correct.
n4140 [dcl.init.list]/1
List-initialization is initialization of an object or reference from a braced-init-list.
You're using list-initialization there, and since c
is an object, the rules for its list-initialization are defined in [dcl.init.list]/3:
[dcl.init.list]/3:
List-initialization of an object or reference of type T is defined as follows:
- If
T
is an aggregate...
- Otherwise, if the initializer list has no elements...
- Otherwise, if
T
is a specialization of std::initializer_list<E>
...
going through the list so far:
Foo
is not an aggregate.
- It has one element.
Foo
is not a specialization of std::initializer_list<E>
.
Then we hit [dcl.init.list]/3.4:
Otherwise, if T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
Now we're getting somewhere. 13.3.1.7 is also known as [over.match.list]:
Initialization by list-initialization
When objects of non-aggregate class type T
are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
- Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class
T
and the argument list consists of the initializer list as a single argument.
- If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class
T
and the argument list consists of the elements of the initializer list.
So the copy constructor will only be considered after the initializer list constructors, in the second phase of overload resolution. The initializer list constructor should be used here.
It's worth noting that [over.match.list] then continues with:
If the initializer list has no elements and T
has a default constructor, the first phase is omitted. In copy-list initialization, if an explicit constructor is chosen, the initialization is ill-formed.
and that after [dcl.init.list]/3.5 deals with single-element list initialization:
Otherwise, if the initializer list has a single element of type E
and either T
is not a reference type or its referenced type is reference-related to E
, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T
, the program is ill-formed.
which explains where cppreference got their special case for single-element list initialization, though they placed it higher in the order than it should be.
Original Answer
You're encountering an interesting aspect of list initialization, where if the list fulfills certain requirements it may be treated like a copy-initialization rather than a list-initialization.
from cppreference:
The effects of list initialization of an object of type T
are:
If T
is a class type and the initializer list has a single element of
the same or derived type (possibly cv-qualified), the object is
initialized from that element (by copy-initialization for
copy-list-initialization, or by direct-initialization for
direct-list-initialization). (since c++14)
Foo c{b}
fulfills all these requirements.
Let us examine what the C++14 specification says about list initialization here. [dcl.init.list]3 has a sequence of rules which are to be applied in order:
3.1 does not apply, since Foo
is not an aggregate.
3.2 does not apply, since the list is not empty.
3.3 does not apply, since Foo
is not a specialization of initializer_list
.
3.4 does apply, since Foo
is a class type. It says to consider constructors with overload resolution, in accord with [over.match.list]. And that rule says to check initializer_list
constructors first. Since your type has an initilaizer_list
constructor, the compiler must check to see if an initializer_list
matching one of those constructors can be manufactured from the given values. It can, so that is what must be called.
In short, GCC is right and Clang is wrong.
It should be noted that the C++17 working draft changes nothing about this. It has a new section 3.1 that has special wording for single-value lists, but that only applies to aggregates. Foo
is not an aggregate, so it does not apply.