Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional operator with same underlying class type

Should this program output 0 or 1? In my reading and understanding of the cited paragraphs from the C++14 standard, it should print 1, but both GCC and clang prints 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 A const lvalue, and std::move(a) is a A xvalue, both of class-types. According to the standard about the conditional operator (emphasis mine), the result should be an lvalue of type A const, and thus the decltype result must be A const&:

[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 a 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 A const, 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, and thus the program should print 1.

like image 713
Peregring-lk Avatar asked Oct 01 '17 19:10

Peregring-lk


People also ask

What is ternary operator in C?

Ternary Operator in C is an operator which takes three operands or variables, unlike the other operators which take one or two operands. Ternary operator in C is also known as the Conditional Operator. It is a way to shorten the simple if-else code of the block.

What is ternary operator example?

It helps to think of the ternary operator as a shorthand way or writing an if-else statement. Here's a simple decision-making example using if and else: int a = 10, b = 20, c; if (a < b) { c = a; } else { c = b; } printf("%d", c); This example takes more than 10 lines, but that isn't necessary.

What is the return type of conditional operator?

Conditional AND The operator is applied between two Boolean expressions. It is denoted by the two AND operators (&&). It returns true if and only if both expressions are true, else returns false.

What is the conditional operator in C?

The conditional operator is also known as a ternary operator. The conditional statements are the decision-making statements which depends upon the output of the expression. It is represented by two symbols, i.e., '?' and ':'. As conditional operator works on three operands, so it is also known as the ternary operator.


1 Answers

The question is awkwardly worded. You should instead ask what the type and value category of the expression true ? ra : std::move(a) should be. The answer to that question is a prvalue of type A const. This subsequently means the program should print 0, as I think every compiler correctly does.


The rules for ?: are fairly complex. In this case, we have two expressions of class type that we try to see if we can convert to each other, based on a limited subset of rules.

Attempting the conversion rastd::move(a) fails. We first try with a target type is A&& which can't bind directly to ra. We then try the backup plan in (3.3.1) since the two expressions have the same underlying class type, but our target expression is not at least as cv-qualified as the source expression, so this also fails.

Attempting the conversion std::move(a)ra fails (3.1) because we need to bind directly to an lvalue (we can bind an rvalue to a const lvalue reference, but here we are required to bind an lvalue). But, the (3.3.1) backup succeeds because now the target type is at least as cv-qualified as the source.

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

(4) fails, because they're not of the same value category.

Hence, the result is a prvalue. And since they have the same type, the result is of that type: A const.

like image 63
Barry Avatar answered Sep 17 '22 03:09

Barry