三元运算符为何以及何时返回左值?

时间:2019-02-14 10:41:14

标签: c++ conditional-operator rvalue lvalue

很长时间以来,我认为三元运算符总是返回一个右值。但令我惊讶的是,事实并非如此。在下面的代码中,我看不到foo的返回值和三元运算符的返回值之间的区别。

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

3 个答案:

答案 0 :(得分:35)

ij都是 glvalues (有关详细信息,请参见this value category reference)。

然后,如果您阅读this conditional operator reference,我们将得出以下结论:

  

4)如果E2和E3是相同类型和相同值类别的glvalue,则结果具有相同的类型和值类别

因此(i < 3) ? i : j的结果是一个glvalue,可以将其分配给它。

但是我不建议这样做。

答案 1 :(得分:19)

[expr.cond]中详细介绍了此规则。对于类型和值类别的几种组合,有很多分支。但最终,在默认情况下,表达式是prvalue。您的示例中的案例已包含在第5段中:

  

如果第二个和第三个操作数是具有相同值的glvalues   类别并具有相同的类型,结果是该类型和值   类别,如果第二个或第三个操作数为a,则为位字段   位字段,或者如果两者都是位字段。

ij都是变量名称,都是类型int的左值表达式。因此,条件运算符会产生一个int左值。

答案 2 :(得分:2)

如果第二和第三操作数的类型为左值,则三进制条件运算符将产生一个左值。

您可以使用功能模板is_lvalue(如下所示)来确定操作数是否为左值,并在功能模板isTernaryAssignable中使用它来确定是否可以将其分配给它。

一个最小的例子:

#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';   
}

输出:

true
false
false
false

LIVE DEMO

注意:您传递给isTernaryAssignable的操作数应不会衰减(例如,一个数组会衰减到指针)。