We know that T v(x);
is called direct-initialization, while T v = x;
is called copy-initialization, meaning that it will construct a temporary T
from x
that will get copied / moved into v
(which is most likely elided).
For list-initialization, the standard differentiates between two forms, depending on the context. T v{x};
is called direct-list-initialization while T v = {x};
is called copy-list-initialization:
§8.5.4 [dcl.init.list] p1
[...] List-initialization can occur in direct-initialization or copy-initialization contexts; list-initialization in a direct-initialization context is called direct-list-initialization and list-initialization in a copy-initialization context is called copy-list-initialization. [...]
However, there are only two more references each in the whole standard. For direct-list-initialization, it's mentioned when creating temporaries like T{x}
(§5.2.3/3
). For copy-list-initialization, it's for the expression in return statements like return {x};
(§6.6.3/2
).
Now, what about the following snippet?
#include <initializer_list>
struct X{
X(X const&) = delete; // no copy
X(X&&) = delete; // no move
X(std::initializer_list<int>){} // only list-init from 'int's
};
int main(){
X x = {42};
}
Normally, from the X x = expr;
pattern, we expect the code to fail to compile, because the move constructor of X
is defined as delete
d. However, the latest versions of Clang and GCC compile the above code just fine, and after digging a bit (and finding the above quote), that seems to be correct behaviour. The standard only ever defines the behaviour for the whole of list-initialization, and doesn't differentiate between the two forms at all except for the above mentioned points. Well, atleast as far as I can see, anyways.
So, to summarize my question again:
What is the use of splitting list-initialization into its two forms if they (apparently) do the exact same thing?
explicit
, would it stop working? – Wassail