Search code examples
c++visual-c++language-lawyermove-semanticsconditional-operator

Moving after copying in assignment of conditional operator result


Simplified program as follows

struct S {
    S() {}
    S(const S &) {}
    S(S &&) = delete;
};

S x;
S y = false ? S() : x;

is accepted just fine by GCC and Clang, but the latest Visual Studio 2022 v17.10 rejects it with the error:

error C2280: 'S::S(S &&)': attempting to reference a deleted function

Online demo: https://gcc.godbolt.org/z/xnc7x6W4v

It appears that MSVC first copies the value of x in a temporary and then moves the temporary into y, unlike other compilers, which do not call S(S &&).

Is the behavior of Microsoft compiler correct according to C++ standard?


Solution

  • MSVC is wrong; GCC and Clang are correct. MSVC incorrectly applies the temporary materialization conversion (i.e. turning S() into an xvalue).

    Looking at false ? S() : x;, this is a conditional operator where both possible expressions are of the same class type, but S() is a prvalue and x is an lvalue. Therefore, the result is a prvalue ([expr.cond] p6).

    Lvalue-to-rvalue conversion is applied to x ([expr.cond] p7), and the declaration of y is effectively:

    S y = false ? S() : S(x);
    

    The relevant bullet is [expr.cond] p7.1.

    The second and third operands have the same type; the result is of that type and the result object is initialized using the selected operand.

    Since C++17, the move constructor is never called here; instead, [dcl.init.general] p16.6.1 applies:

    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.

    S(x) is used to initialize the destination object y. The move constructor is never called, and temporary materialization does not take place.