【问题标题】:Value category of conditional operator条件运算符的值类别
【发布时间】:2019-04-19 02:01:24
【问题描述】:

考虑以下代码:

int x;
int& f() {
  return x ? x : throw 0;
}

使用gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04) 我得到以下编译错误:

cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’

请注意,这在 clang 中编译得很好。这是(我认为是)标准中的相关声明:

N4659 [8.16.2.1](条件运算符):
第二个或第三个操作数(但不是两者)是一个(可能带括号的)抛出表达式(8.17);结果是对方的类型和值类别。

据我了解,x 是一个左值,所以在我看来clang 是对的。我错了吗?


如果我不得不猜测,左值到右值的转换正在发生,因为条件中的两个表达式没有相同的类型,但是因为第二个是抛出,所以这个转换应该被抢占。我不熟悉提交错误报告,但也许这会是一个更好的论坛。
以下是关于条件运算符的一些(可能)更有用的问题:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)

【问题讨论】:

  • 显然是g++中的一个bug,Shafik提到的变化出现在C+14以后的标准中

标签: c++ g++ language-lawyer conditional-operator value-categories


【解决方案1】:

clang 在这里是正确的,旧的行为是无条件地将值转换为 prvalue,看起来 gcc 仍在实现。

这是DR 1560 的主题,已通过DR 1550 的分辨率修复。 DR 1560 说:

作为条件表达式的一个操作数出现的左值 另一个操作数是 throw-expression 被转换为 prvalue,无论如何使用条件表达式:

如果第二个或第三个操作数的类型为 void,则左值到右值 (7.1 [conv.lval])、数组到指针 (7.2 [conv.array]) 和函数到指针 (7.3 [conv.func]) 标准 转换是在第二个和第三个操作数上执行的,其中之一 以下内容应成立:

  • 第二个或第三个操作数(但不是两者)是 throw 表达式 (18.1 [except.throw]);结果的类型是 另一个是prvalue。

这似乎是无缘无故和令人惊讶的。

并且DR 1550[expr.cond] 中的措辞更改为我们现在的措辞:

第二个或第三个操作数(但不是两者)是一个(可能带括号的)抛出表达式;结果是另一个的类型和值类别。如果操作数是位域,则条件表达式就是位域。

所以看起来 gcc 正在实施旧行为,而 clang 正在实施 DR。

这是patch that applied DR 1560 to clang。它添加了以下测试:

namespace DR1560 { // dr1560: 3.5
  void f(bool b, int n) {
    (b ? throw 0 : n) = (b ? n : throw 0) = 0;
  }
  class X { X(const X&); };
  const X &get();
  const X &x = true ? get() : throw 0;
}

on godbolt we can see this fails for gcc 是因为:

error: lvalue required as left operand of assignment
4 |     (b ? throw 0 : n) = (b ? n : throw 0) = 0;
  |                    

我们有一个 gcc bug report for a very similar issue,它有以下简化的测试用例:

我想解决这个错误并提供一个更简单的测试用例:

void blah(int&) {}

int main() {
    int i{};
    blah(true ? i : throw);
}

gcc 6.0 的结果:

prog.cc: In function 'int main()':
prog.cc:6:15: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
     blah(true ? i : throw 0);
          ~~~~~^~~~~~~~~~~~~

【讨论】:

    猜你喜欢
    • 2018-07-04
    • 2018-06-09
    • 1970-01-01
    • 2011-03-11
    • 2015-06-06
    • 2014-01-29
    • 1970-01-01
    • 2011-09-18
    相关资源
    最近更新 更多