简体   繁体   中英

I think there might be a small inaccuracy in bullet point (17.6.2) in §8.5[dcl.init]/17 in N4140

In bullet point (17.6.2) in §8.5[dcl.init]/17 in N4140 we have (emphasis is mine):

Otherwise (ie, for the remaining copy-initialization cases ), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

The portion of the text in bold seems to indicate that direct-initializations will never invoke a user-defined conversion sequence. But that's not what I found out below:

#include <iostream>

struct A {
    A() { std::cout << "default ctor A" << '\n'; }
    A(const A&) { std::cout << "copy A" << '\n'; }
};

struct C {
    C() { std::cout << "default ctor C" << '\n'; }
    operator A() { std::cout << "C::operator A()" << '\n'; return A(); };
};

int main()
{
     C c;
     A a{c};        // direct-initialization where the C::operator A() is invoked
                    // to copy construct the object `a`.
}

The following is printed by this snippet:

default ctor C
C::operator A()
default ctor A
copy A
copy A

See live example

Edit

In response to @Johannes answer, please consider A a(c); instead of A a{c}; . The rest remains valid, as far as I can understand.

When discussing direct initialization (emphasis is my own):

If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3) . The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.

This line leads to section §8.5 (2.8,2.9) on Overload Resolutions which describes the process.

Overload resolution selects the function to call in seven distinct contexts within the language:

... (2.4) — invocation of a constructor for direct-initialization (8.5) of a class object...

But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:

(2.8) — First, a subset of the candidate functions (those that have the proper number of arguments and meet certain other conditions) is selected to form a set of viable functions (13.3.2).

(2.9) — Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

Given the above, direct initialization does in fact go through the process of selecting candidate constructors (in your case, the one parameter constructor A::A( A const &) ) and finding an implicit converse sequence to match the two together - that is operator C::A() .

It is somewhat misleading that they apply - what seems to be - redundant language in the text for other copy-initialization cases.

The extra language on an implicit conversion sequence - that you quote in the question - for the other copy-initialization cases of (17.6.2) directly refer to the following case:

class B {
    operator A();
};
class A {

};

B b;
A a = b;

This isn't covered under the first point in (17.6.1) as B doesn't derive from A nor do any helpful constructors exist. Therefore it falls under the 'other' clause.

I don't understand your concern, but answering what happens, in case this can solve it for you. A a{c} directly branches off of bullet 1 of dcl.initp17 to dcl.init.list.

From there it happens that it will initialize a const A& from a C (by over.match.list selecting the copy-constructor of A ). This will invoke the paragraph you cited (by first branching off to dcl.init.ref and then applying dcl.initp17 in turn). This will construct a normal user-defined conversion sequence which is able to use conversion functions, such as your operator A .

I think you are confused by the fact that it only mentions user defined conversion sequences for the copy initializations, but in the preceeding bullet it doesn't. The reason is that when doing direct initialization, that is conceptual a direct call to the constructors of a class instead of being a conversion (in which the call to constructors is only a part, surrounded by a standard conversion sequence before (for constructors) and after (for conversion functions/operators) it). In other words, for direct initialization, you already force the constructor call, rather than making the compiler find out that it needs to do it. And you haven't "paid" a user defined conversion sequence (you cannot nest user defined conversion sequences, so they are "precious") - the compiler can still apply a user defined conversion sequence for matching up the argument for the constructor.

The proper testcase for this would be A a(c) rather than A a{c} though, to prevent branching off to dcl.init.list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM