A conditional statement with the same type of base class

Should this program print 0 or 1? In my reading and understanding of the quoted paragraphs from the C ++ 14 standard, it should print 1, but both GCC and clang print 0 (because the deduced type is A const instead of A const& ):

 #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 0 } 

In this case, ra is A const lvalue, and std::move(a) is A xvalue, both types of the class. According to the standard on the conditional operator (my selection), the result should be an lvalue type A const , and therefore, the decltype result should be A const& :

[expr.cond] / 3 Otherwise, if the second and third operands are of different types but have a (possibly cv-qualified) class of type , or if both are values ​​of the same category of values ​​of the same type, except cv-qualifications, an attempt is made to convert each of these operands to the type of the other. The process of determining whether it is possible to transform the expression E1 of the operand of type T1 into correspondence with the expression of the operand E2 of type T2 is defined as follows:

- If E2 is an lvalue : E1 can be converted to E2, if E1 can be implicitly converted (clause 4) to type "lvalue reference to T2" , provided that the link must be directly linked (8.5.3) with lvalue during conversion .

[...]

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

[expr.cond] / 3 If the second and third operands are glvalues ​​of the same category of values ​​and have the same type, the result refers to the type and category of values ​​[...].

So, the result of the statement should be lvalue, and the result of decltype should be a reference, and therefore, the program should print 1.

+5
source share
1 answer

The question is awkwardly worded. Instead, you should ask, what should be the category category and value of the expression true ? ra : std::move(a) true ? ra : std::move(a) . The answer to this question is a prvalue of type A const . Subsequently, this means that the program should print 0, as I think every compiler does the right thing.


The rules for ?: pretty complicated. In this case, we have two class type expressions that we are trying to see if we can transform each other based on a limited subset of the rules.

An attempt to convert ra β†’ std::move(a) fails. First we try with target type A&& , which cannot directly communicate with ra . Then we try the backup plan in (3.3.1) , because the two expressions have the same base class type, but our target expression is not at least cv-qualified as the original expression, so this also fails.

The conversion attempt std::move(a) β†’ ra fails (3.1) , because we need to bind directly to the lvalue (we can bind the rvalue value for the const lvalue reference, but here we are required to bind the lvalue). But recovery (3.3.1) is successful, because now the target type is at least as cv-qualified as the source.

Therefore, we apply the transformation and continue as if the second operand was an lvalue of type A const , but the third operand is now a prvalue of type A const (instead of xvalue of type A ).

(4) fails because they do not belong to the same category of meanings.

Therefore, the result is a prvalue value . And since they are of the same type, the result is of this type : A const .

+6
source

Source: https://habr.com/ru/post/1272267/


All Articles