Soulimane Mammar
Soulimane Mammar

Reputation: 1858

Why and when does the ternary operator return an lvalue?

For a long time I thought that the ternary operator always returns an rvalue. But to my surprise it doesn't. In the following code I don't see the difference between the return value of foo and the return value of the ternary operator.

#include <iostream>
int g = 20 ;

int foo()
{
    return g ;
}

int main()
{
    int i= 2,j =10 ;

    foo()=10 ; // not Ok 
    ((i < 3) ? i : j) = 7; //Ok
    std::cout << i <<","<<j << "," <<g << std::endl ;
}

Upvotes: 43

Views: 5537

Answers (3)

P.W
P.W

Reputation: 26800

Ternary conditional operator will yield an lvalue, if the type of its second and third operands is an lvalue.

You can use the function template is_lvalue (below) to find out if an operand is an lvalue and use it in the function template isTernaryAssignable to find out if it can be assigned to.

A minimal example:

#include <iostream>
#include <type_traits>

template <typename T>
constexpr bool is_lvalue(T&&) {
  return std::is_lvalue_reference<T>{};
}

template <typename T, typename U>
bool isTernaryAssignable(T&& t, U&& u)
{
    return is_lvalue(std::forward<T>(t)) && is_lvalue(std::forward<U>(u));
}

int main(){
    int i= 2,j =10 ;

    ((i < 3) ? i : j) = 7; //Ok

    std::cout << std::boolalpha << isTernaryAssignable(i, j); std::cout << '\n';
    std::cout << std::boolalpha << isTernaryAssignable(i, 10); std::cout << '\n';
    std::cout << std::boolalpha << isTernaryAssignable(2, j); std::cout << '\n';
    std::cout << std::boolalpha << isTernaryAssignable(2, 10); std::cout << '\n';   
}

Output:

true
false
false
false

LIVE DEMO

Note: The operands you pass to isTernaryAssignable are to be such that they will not undergo decay (For example an array which decays to pointer).

Upvotes: 3

Some programmer dude
Some programmer dude

Reputation: 409166

Both i and j are glvalues (see this value category reference for details).

Then if you read this conditional operator reference we come to this point:

4) If E2 and E3 are glvalues of the same type and the same value category, then the result has the same type and value category

So the result of (i < 3) ? i : j is a glvalue, which can be assigned to.

However doing something like that is really not something I would recommend.

Upvotes: 40

The rules for this are detailed in [expr.cond]. There are many branches for several combinations of types and value categories. But ultimately, the expression is a prvalue in the default case. The case in your example is covered by paragraph 5:

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 and it is a bit-field if the second or the third operand is a bit-field, or if both are bit-fields.

Both i and j, being variables names, are lvalue expressions of type int. So the conditional operator produces an int lvalue.

Upvotes: 22

Related Questions