Reputation: 1858
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
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
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
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
Reputation: 170065
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