5

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?

Wake up Brazil
  • 3,421
  • 12
  • 19
  • 2
    You're asking one question in the title, and a different one in the body. You should ask one clear question. – juanchopanza Aug 23 '16 at 20:35
  • OK, if you insist on leaving two largely unrelated questions in your post, you could at least explain why you think it could be ill-formed and how that relates to direct initialization. – juanchopanza Aug 23 '16 at 21:28
  • @juanchopanza I've already explained this in several comments below. The initialization of `a3` doesn't use a direct-initialization syntax as described in {[dcl.init]/16}(http://eel.is/c++draft/dcl.init#16). Thus, according to {[class.conv.ctor]/2}(http://eel.is/c++draft/class.conv.ctor#2) this initialization should be invalid. – Wake up Brazil Aug 23 '16 at 21:33
  • and as explained in several comments below, it is copy-initialized from a direct-initialized prvalue. The direct-initialization of the prvalue is what the comment is referring to. – jaggedSpire Aug 23 '16 at 21:35
  • 1
    Why are you so insistent on not clarifying the question itself? Do you think future readers want to trawl through all the comments to make sense of your "question"? Also, your "explanations" are full of false premises. The fact that the initialization of `a3` isn't direct initialization does not make the code ill formed, as has been already explained to you endless times. – juanchopanza Aug 23 '16 at 21:35
  • @juanchopanza The Note in [class.conv.ctor]/2 is pretty clear to me (emphasis is mine): `An explicit constructor constructs objects just like non-explicit constructors, but does so **only** where the direct-initialization syntax ([dcl.init]) or where casts ([expr.static.cast], [expr.cast]) are explicitly used;`. – Wake up Brazil Aug 23 '16 at 21:39
  • Jeez. `a3` uses copy initialization from a prvalue on the RHS. The prvalue is initialized with direct initialization. Oh, wait. You've already been told this. – juanchopanza Aug 23 '16 at 21:42
  • @WakeupBrazil you're reading it wrong. – jaggedSpire Aug 23 '16 at 21:44
  • @jaggedSpire I'm genuinely trying to understand this. If you care to explain where is my fault, I would appreciate. For instance, where am I reading wrong? – Wake up Brazil Aug 23 '16 at 21:48
  • 1
    @WakeupBrazil It has been explained over and over in two answers and some of the comments. I really don't know what else can be added. – juanchopanza Aug 23 '16 at 21:52
  • 1
    the "only" in that phrase is referring to the sub-expression where a value is initialized using the explicit constructor. It is *not* saying that *only* direct-initialization may be used in the *full-expression* where an explicit constructor is used. The sub-expression using the explicit constructor is `Z(1)`. The presence of this sub-expression which uses an explicit constructor in the full-expression which contains it, `Z a3 = Z(1);` does not prohibit other kinds of construction in other parts of the full-expression. ... – jaggedSpire Aug 23 '16 at 22:03
  • 1
    The `Z1 a3 = ` part is a different part of the full-expression. It does not use the explicit constructor. It uses the *result* of the explicit constructor, which is not the same thing. Just like `Z b(1); Z c = b;` does not use an explicit constructor when constructing `c`, the expression `Z a3 = x;` where X is *anything* that results in a `Z` does not use the explicit constructor to create `a3`. It uses copy-construction. – jaggedSpire Aug 23 '16 at 22:04

3 Answers3

8

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.

Community
  • 1
  • 1
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • But the initialization compiles in C++11 and C++14 (clang and GCC), i.e., before the rules of guaranteed elision were standardized. – Wake up Brazil Aug 23 '16 at 20:06
  • 3
    @WakeupBrazil Why would you expect it not to compile? BTW it should compile with c++98 too. – juanchopanza Aug 23 '16 at 20:09
  • @juanchopanza Because that's not direct-initialization. See {[dcl.init]/16}(http://eel.is/c++draft/dcl.init#16). – Wake up Brazil Aug 23 '16 at 20:13
  • 1
    @WakeupBrazil So what? It is still valid code. It doesn't have to be direct initialization to be valid. – juanchopanza Aug 23 '16 at 20:14
  • @juanchopanza Have you noticed that constructors are explicit? – Wake up Brazil Aug 23 '16 at 20:15
  • 1
    @WakeupBrazil Yes, I have. But that doesn't matter here. Maybe you can clarify in your question why you think the code could be invalid. And clarify what you're actually asking. – juanchopanza Aug 23 '16 at 20:18
  • @juanchopanza For me `Z a3 = Z(1);` doesn't use direct-initialization syntax. – Wake up Brazil Aug 23 '16 at 20:21
  • 2
    @WakeupBrazil Sigh. So what? It. does. not. matter. And direct initialization refers to `Z(1)`, as has already been explained in the answers. – juanchopanza Aug 23 '16 at 20:22
  • The initialization in the example also uses copy-initialization, and AFAICU that's not part of a direct-initialization **syntax**. – Wake up Brazil Aug 23 '16 at 20:50
  • 3
    @WakeupBrazil: I'm not sure what you're talking about here. `Z(1)` is direct initialization syntax, as clearly stated by the link you keep posting. `Z a3 = ...` performs copy initialization, which may or may not be elided, but either way will not invoke any explicit constructors. – Nicol Bolas Aug 23 '16 at 20:58
  • I'm talking about the syntax used to initialize `a3`. That is not direct-initialization, as described in {[dcl.init]/16}(http://eel.is/c++draft/dcl.init#16). – Wake up Brazil Aug 23 '16 at 21:02
  • @WakeupBrazil does [this](http://coliru.stacked-crooked.com/a/2880b0e9881fabb7) make it any clearer? – jaggedSpire Aug 23 '16 at 21:11
  • 3
    @WakeupBrazil: "*I'm talking about the syntax used to initialize a3.*" It doesn't matter what *you* are talking about. Because the *comment* is talking about the syntax used to initialize the temporary. – Nicol Bolas Aug 23 '16 at 21:49
  • `By the rules of guaranteed elision, there is no temporary-followed-by-copy.` Could you explain why doesn't this compile? `struct Z { explicit Z(int); Z(Z const&) = delete; }; Z a3 = Z(1);` – Wake up Brazil Aug 27 '16 at 12:32
  • @WakeupBrazil: Because your compiler isn't following the C++17 rules of guaranteed elision. – Nicol Bolas Aug 27 '16 at 13:59
  • What about GCC and clang? They don't compile this with C++1z. – Wake up Brazil Aug 27 '16 at 20:36
  • @WakeupBrazil: What about them? Do GCC and Clang claim that they support guaranteed elision yet? No; [they do not](http://en.cppreference.com/w/cpp/compiler_support). Just because there's a switch doesn't mean that they support *everything* in the standard. Especially since the standard *isn't finished yet*. The switch simply turns on whatever C++17 features that they have currently implemented. And this isn't one of them. – Nicol Bolas Aug 27 '16 at 20:39
  • Thanks for the link – Wake up Brazil Aug 27 '16 at 21:22
4

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.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Now it makes sense to me. They are saying the line is okay because `Z(1)` is directly initialized(which it must be as the constructors are explicit). – NathanOliver Aug 23 '16 at 20:17
  • How come `Z a3 = Z(1);` uses a direct-initialization syntax? – Wake up Brazil Aug 23 '16 at 20:19
  • 4
    @WakeupBrazil The `Z(1)` part of it does. And I even gave you the exact quote that says it does... – T.C. Aug 23 '16 at 20:22
  • {[dcl.init]/16}(http://eel.is/c++draft/dcl.init#16) explains what is a direct-initialization syntax, and that is not what I can see in the alluded example. – Wake up Brazil Aug 23 '16 at 20:26
  • @WakeupBrazil Yes, I know what that paragraph says. I even quoted the pertinent part of it. /sigh – T.C. Aug 23 '16 at 20:40
  • But the initialization in the example also uses copy-initialization, and AFAICU that's **not** part of a direct-initialization syntax. – Wake up Brazil Aug 23 '16 at 20:46
  • 2
    @WakeupBrazil So it uses both. So what? If I say "the letter B is used in your username", do you complain that it also uses a bunch of other letters? – T.C. Aug 23 '16 at 20:50
  • just because a different part of the expression uses a different syntax doesn't mean the `Z(1)` part doesn't use direct-initialization. The expression can use more than one type of syntax. "direct initialization syntax used" does not imply that other types are not *also* used, merely that direct initialization is one of those syntaxes used. – jaggedSpire Aug 23 '16 at 20:51
  • @jaggedSpire `The expression can use more than one type of syntax`. Not according to {[class.conv.ctor]/2}(http://eel.is/c++draft/class.conv.ctor#2). – Wake up Brazil Aug 23 '16 at 20:54
  • that doesn't say that. Also, it's a note. – jaggedSpire Aug 23 '16 at 20:56
  • I don't see yet why there would neet to be a copy constructor or move constructor. Doesn't the prvalue initialize the object `z3` directly? There is no object in between which may need to be copied here. The relation of prvalue to object seems to be directly "Z(1) -> identity(z3)", here. – Johannes Schaub - litb Aug 25 '16 at 08:57
  • @JohannesSchaub-litb Yes, it doesn't need either in C++17 due to guaranteed elision. That part is addressing "or not", i.e., OP's comment on Nicol's answer about C++14 and earlier, which does require a suitable constructor. – T.C. Aug 25 '16 at 10:38
2

(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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212