简体   繁体   中英

Is an implicit call to a conversion function categorised as an explicit call (prvalue, xvalue, lvalue, etc.)?

(c++20 Working Draft N4868)

When an implicit call is made to conversion constructor in copy-initialization of an class, the spec says that the call is an prvalue expression (bold mine) [dcl.init.general]/16.6.3.

Otherwise (ie, for the remaining copy-initialization cases), user-defined conversions 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 12.4.2.5, and the best one is chosen through overload resolution (12.4). 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 is a prvalue of the cv-unqualified version of the destination type whose result object is initialized by the constructor. The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization

But what for copy-initialization of both class-type and initialization of fundamental types using conversion function? The quote above don't says clearly (class-type) and the quote below, that is for the fundamental types, too don't says clearly (bold mine) [dcl.init.general]/16.7:

Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (12.4.2.6), and the best one is chosen through overload resolution (12.4). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized . If the conversion cannot be done or is ambiguous, the initialization is ill-formed.

Snippet

struct MyClass {
    int m_int {};
    double m_double {};

    MyClass() = default;

    MyClass(double x) {
        //nothing
    }

    operator double() {
        return m_double;
    }

    operator int&() {
        return m_int;
    }
};

int main() {
    MyClass object;

    int& x = object; 
        //Is the implicit call to conversion function a lvalue expression like static_cast<int&>(object)?

    double y = object; 
        //Is the implicit call to conversion function a prvalue expression like static_cast<double>(object)?

    MyClass other_object = 4.0; 
        //The implicit call to conversion constructor is prvalue like MyClass(4.0);
}

Question:

Does an implicit call to conversion function is categorized like an explicit call (prvalue, xvalue, lvalue, etc.)?


My thoughts:

Any implicit conversion has an expression category

[conv.general]/6 (bold mine)

The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type (9.3.4.3), an xvalue if T is an rvalue reference to object type, and a prvalue otherwise. The expression E is used as a glvalue if and only if the initialization uses it as a glvalue

and [conv.general]/7 (bold mine)

[Note 3: For class types, user-defined conversions are considered as well ; see 11.4.8. In general, an implicit conversion sequence (12.4.4.2) consists of a standard conversion sequence followed by a user-defined conversion followed by another standard conversion sequence. — end note]

Any function call has an expression category

[expr.call]/14

A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.

I think that the rules are the same (implicit and explicit), but I'm not sure on my interpretation.

The clang AST (Abstract Syntax Tree) for the snippet suggests that also ( https://godbolt.org/z/W5MPbGzq8 ):

`-FunctionDecl <line:20:1, line:31:1> line:20:5 main 'int ()'
  `-CompoundStmt <col:12, line:31:1>
    |-DeclStmt <line:21:5, col:19>
    | `-VarDecl <col:5, col:13> col:13 used object 'MyClass' callinit
    |   `-CXXConstructExpr <col:13> 'MyClass' 'void () noexcept'
    |-DeclStmt <line:23:5, col:20>
    | `-VarDecl <col:5, col:14> col:10 x 'int &' cinit
    |   `-ImplicitCastExpr <col:14> 'int' lvalue <UserDefinedConversion>
    |     `-CXXMemberCallExpr <col:14> 'int' lvalue
    |       `-MemberExpr <col:14> '<bound member function type>' .operator int & 0x55982b410db8
    |         `-DeclRefExpr <col:14> 'MyClass' lvalue Var 0x55982b411308 'object' 'MyClass'
    |-DeclStmt <line:26:5, col:22>
    | `-VarDecl <col:5, col:16> col:12 y 'double' cinit
    |   `-ImplicitCastExpr <col:16> 'double' <UserDefinedConversion>
    |     `-CXXMemberCallExpr <col:16> 'double'
    |       `-MemberExpr <col:16> '<bound member function type>' .operator double 0x55982b410c38
    |         `-DeclRefExpr <col:16> 'MyClass' lvalue Var 0x55982b411308 'object' 'MyClass'
    `-DeclStmt <line:29:5, col:31>
      `-VarDecl <col:5, col:28> col:13 other_object 'MyClass' cinit
        `-ImplicitCastExpr <col:28> 'MyClass' <ConstructorConversion>
          `-CXXConstructExpr <col:28> 'MyClass' 'void (double)'
            `-FloatingLiteral <col:28> 'double' 4.000000e+00

Related

Is the move constructor called after invoking a conversion function? .

In my interpretation , T.C. suggests that the implicit call to conversion function is a prvalue expression and initialize the result object, not invoking the move-constructor (suggesting copy-elision)

I don't understand the confusion. Of course if a conversion function is called implicitly as required by the language, the value category of the result of such call is the same as the value category that would be obtained from an explicit call to that conversion function ( ie , a prvalue if the return type is a non-reference, an lvalue if it is an lvalue reference to object or any reference to function, or an xvalue if it is an rvalue reference to object). What does it matter that this isn't stated explicitly? Why would you think that there is any other possibility?

[conv.general]/6 does not control the meaning of [dcl.init]/16.6.3, and is thus irrelevant here. (It is, rather, the other way around: to understand the meaning of an implicit conversion, you need to understand what the corresponding copy-initialization does. But only after that copy-initialization is complete---including any implicit conversion function call that was used to initialize the target of the initialization---[conv.general]/6 tells you the value category of the result of the implicit conversion. It tells you nothing about the value category of the result of the implicit call to the conversion function.)

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