The following type has three constructors. Note that one of them takes an initializer list of elements of that very same type.
struct Foo {
Foo() {
std::cout << "default ctor" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer_list<Foo>" << std::endl;
}
};
While initializing an object using an initializer_list, I am surprised to see the copy constructor is automatically called as many times as there are elements in the initializer_list. Afterwards, the initializer_list constructor is called:
int main()
{
Foo a; // default ctor
Foo b{a, a, a}; // copy ctor + copy ctor + copy ctor + initializer_list<Foo>
return 0;
}
What are the reasons/justifications behind that behavior? Note that if Foo had no copy constructor, the initialization Foo b{a, a, a}
would apparently be perfectly possible (the initializer_list constructor would be the only one called).
Full code here: http://coliru.stacked-crooked.com/a/f6e28dbb66746aa2
What are the reasons/justifications behind that behavior?
The expression {a, a, a}
(in your example) constructs a std::initializer_list<Foo>
.
Each element of that array (yeah, you can intend an initializer list like a lightweight array) is construct as a copy of the object a
.
Indeed, the copy constructor of that class is called three times in order to construct exactly three copies.
Note that if Foo had no copy constructor, the initialization Foo b{a, a, a} would apparently be perfectly possible (the initializer_list constructor would be the only one called).
That's no true. If Foo
"has no copy constructor", the compiler will provide a default one. So in that case the copy constructor will be still called three times, as in the previous example.
You can prove it just deleting the default copy constructor.
From your example:
struct Bar {
// ...
Bar(const Bar&) = delete;
};
In this way, there is no copy constructor and code will not compile because of the error:
use of deleted function 'Bar::Bar(const Bar&)'
std::cout << "d : "; Bar d{c, c, c};
Indeed, it is not possible to construct the initializer list as usual.
Final Conclusions
There is no mystery in that.
You want to construct a "list" of three object. The compiler has to construct those object making copies from a
.