dimanche 1 octobre 2017

Ternary operator with same underlying class type

Consider this code:

#include <iostream>

struct A {};

int main()
{
    A a;
    A const& ra = std::move(a); // #1

    std::cout << std::is_same<decltype(true ? ra : std::move(a)),
                              A const>::value; // Prints 1
}

In this case, ra is a A const lvalue, and std::move(a) is a A xvalue, both of class-types. According to the standard about the ternary operator (emphasis mine), the result should be an lvalue of type const A, and thus the decltype result must be an reference to const A:

[expr.cond]/3 Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:

— If E2 is an lvalue: E1 can be converted to match E2 if E1 can be implicitly converted (Clause 4) to the type “lvalue reference to T2”, subject to the constraint that in the conversion the reference must bind directly (8.5.3) to an lvalue.

[...]

In this case, E2 is ra, which is an lvalue, and the other can be implicitely converted to "lvalue reference to T2", as shown in line // #1. "lvalue reference to T2" is translated as A const&, so, std::move(a) binds directly to a lvalue of type const A, and after the conversion, both operands have same type and value category, and thus:

[expr.cond]/3 If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category [...].

So, the operator result should be an lvalue and the decltype result should be a reference instead of a class type.

Aucun commentaire:

Enregistrer un commentaire