The example in [class.conv.ctor]/2 contains the following initialization:
Z a3 = Z(1); // OK: direct initialization syntax used
How is this considered a direct-initialization syntax?
The example in [class.conv.ctor]/2 contains the following initialization:
Z a3 = Z(1); // OK: direct initialization syntax used
How is this considered a direct-initialization syntax?
Z(1)
will direct-initialize a prvalue. The prvalue will then be used to initialize an object. By the rules of guaranteed elision, there is no temporary-followed-by-copy. The prvalue initializes the object directly. Therefore, Z a3 = Z(1);
is exactly equivalent to Z a3(1);
.
In pre-C++17, this would perform direct initialization of a prvalue temporary, followed by a (almost certainly elided) copy of the temporary into the object a3
. Whether the copy is elided or not, the initialization of the prvalue is via direct initialization. The initialization of a3
is by copy-initialization, but this is through the copy constructor, which is not explicit
.
It's talking about Z(1)
. [dcl.init]/16:
The initialization that occurs in [...] functional notation type conversions (5.2.3) [...] is called direct-initialization.
The prvalue is then used to copy-initialize z
, which is just fine, guaranteed elision or not - Z
's copy/move constructors aren't explicit
anyway, so the initialization is fine even without the guaranteed elision in C++17.
(Word of caution: I'm not very familiar with the new concepts yet, but the following analysis seems to be correct). First you need to start with the functional-cast expression Z(1);
. Note that this prvalue expression is said to initialize some result object using direct-initialization. Which this object is, is left unspecified and decided later, not by this functional-cast expression!
Now, about result objects of prvalues
The result object of a prvalue is the object initialized by the prvalue; ... [... For a discarded prvalue, a temporary object is materialized; see Clause [expr]. ... ]
Let's first make an example for the case Z(1);
which should help understand the concept. This is an expression statement, which is a discarded value expression. For such expressions, a rule says
... If the expression is a prvalue ..., the temporary materialization conversion ([conv.rval]) is applied.
And whenever this "temporary materialization" conversion is applied, a temporary object is created and initialized from that prvalue.
This conversion initializes a temporary object ([class.temporary]) of type T from the prvalue by evaluating the prvalue with the temporary object as its result object, and produces an xvalue denoting the temporary object
Wow, all this is needed for a simple statement like Z(1);
. Now about your case, Z a3 = Z(1);
. For this, the result object is more directly specified by 8.6p17
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.
Viola, there we have an expression whose semantics is "direct-initialize a yet unknown object X by 1", and then another rule supplies this object "X". In particular, the pre-C++17 model that each class-prvalue epxression directly creates objects is outdated. This in particular means that there is no copy constructor or move constructor involved, but your code is exactly equivalent to Z a3(1)
, as far as I can see.