Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the standard differentiate between direct-list-initialization and copy-list-initialization?

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 deleted. 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?

like image 907
Xeo Avatar asked Nov 19 '12 19:11

Xeo


People also ask

What is copy list initialization?

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy initialization.

What is direct initialization?

Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and non-explicit user-defined conversion functions, while direct-initialization considers all constructors and all user-defined conversion functions.

What is assignment and copy initialization?

Direct Initialization or Assignment Operator (Syntax) This assigns the value of one object to another object both of which are already exists. Copy initialization is used when a new object is created with some existing object.


1 Answers

Because they don't do the exact same thing. As stated in 13.3.1.7 [over.match.list]:

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

In short, you can only use implicit conversion in copy-list-initialization contexts.

This was explicitly added to make uniform initialization not, um, uniform. Yeah, I know how stupid that sounds, but bear with me.

In 2008, N2640 was published (PDF), taking a look at the current state of uniform initialization. It looked specifically at the difference between direct initialization (T{...}) and copy-initialization (T = {...}).

To summarize, the concern was that explicit constructors would effectively become pointless. If I have some type T that I want to be able to be constructed from an integer, but I don't want implicit conversion, I label the constructor explicit.

Then somebody does this:

T func() {   return {1}; } 

Without the current wording, this will call my explicit constructor. So what good is it to make the constructor explicit if it doesn't change much?

With the current wording, you need to at least use the name directly:

T func() {   return T{1}; } 
like image 50
Nicol Bolas Avatar answered Oct 07 '22 10:10

Nicol Bolas