Reading again and again the standard, I think this is a bug.
What does the standard say ?
8.5.1/2 : When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken
as initializers for the members of the aggregate, in increasing
subscript or member order. Each member is copy-initialized from the
corresponding initializer-clause.
It is explained that:
8.5/14 : (...) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). —end note ]
But I found no evidence in 12.8 that in your specific case a move would be required.
8.5.4/3 Otherwise, if T is a class type, constructors are considered. If T has an initializer-list constructor, the argument
list consists of the initializer list as a single argument; otherwise,
the argument list consists of the elements of the initializer list.
The applicable constructors are enumerated and the best one is chosen
through overload resolution (13.3).
So in principle you code should work !
Is it a bug ? Trying the experimental way
I commented out the delete of the move constructor, to benefit from the implicit move constructor. Strangely, I then got the following error message:
Compilation error time: 0 memory: 3232 signal:0
prog.cpp: In constructor 'Aggr::Aggr()':
prog.cpp:19:28: error: use of deleted function 'A::A(const A&)'
Aggr() : arr{{"a"}, {"b"}} {}
^
prog.cpp:10:3: note: declared here
A(const A&) = delete
So now he complains about a missing copy constructor !
Even more strangely, I then provided my own move constructor instead of the implicit one : here it compiled the code successfully !!
Finally I provided both a copy and a move and added some tracing:
class A {
private:
std::string s;
public:
A() = delete;
A(const A&) { std::cout<<"copy\n";} //= delete;
A(A&&) { std::cout<<"move\n";} //= delete;
A(const std::string &a) : s(a) { std::cout<<"string ctor\n";}
};
And when I create an Aggr
object, it just displays:
string ctor
string ctor
showing that the array member is initialized form the string constructor using copy elision as we would have expected.
All these tests were performed with gcc-9.4.2 on ideone with C++14 option.
Conclusion
The fact that the same code fails to compile with implicit move ctor and succeeds with a user-defined move ctor looks very seriously like a bug.
The fact that the move constructor is not used when it's available reinforces this impression.
Consequently, I've reported this bug.
std::string
member here makes the destructor non-trivial, just like the user-defined destructor does in the other example. It's the same bug, I've closed it as a duplicate. – Bicolor