条件运算符的值类别
Value category of conditional operator
考虑以下代码:
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] (Conditional Operator):
The second or the third operand (but not both) is a (possibly parenthesized) throw-expression (8.17); the result is of the type and value category of the other.
据我了解,x
是一个左值,所以在我看来clang
是对的。我错了吗?
如果我不得不猜测的话,会发生左值到右值的转换,因为条件表达式中的两个表达式不具有相同的类型,但是因为第二个是抛出,所以应该抢占此转换。我不熟悉提交错误报告,但也许这会是一个更好的论坛。
以下是一些(可能)更有用的关于条件运算符的问题:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)
clang 在这里是正确的,旧的行为是无条件地将值转换为 prvalue,看起来 gcc 仍在实现。
这是 DR 1560 which was fixed by the resolution of DR 1550 的主题。 DR 1560 说:
A glvalue appearing as one operand of a conditional-expression in
which the other operand is a throw-expression is converted to a
prvalue, regardless of how the conditional-expression is used:
If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2
[conv.array]), and function-to-pointer (7.3 [conv.func]) standard
conversions are performed on the second and third operands, and one of
the following shall hold:
- The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of
the other and is a prvalue.
这似乎是无端的和令人惊讶的。
和 DR 1550
将 [expr.cond] 中的措辞更改为我们现在的措辞:
The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.
所以看起来 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,它有以下简化的测试用例:
I wanted to bump this bug and supply a simpler test-case:
void blah(int&) {}
int main() {
int i{};
blah(true ? i : throw);
}
result with 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);
~~~~~^~~~~~~~~~~~~
考虑以下代码:
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] (Conditional Operator):
The second or the third operand (but not both) is a (possibly parenthesized) throw-expression (8.17); the result is of the type and value category of the other.
据我了解,x
是一个左值,所以在我看来clang
是对的。我错了吗?
如果我不得不猜测的话,会发生左值到右值的转换,因为条件表达式中的两个表达式不具有相同的类型,但是因为第二个是抛出,所以应该抢占此转换。我不熟悉提交错误报告,但也许这会是一个更好的论坛。
以下是一些(可能)更有用的关于条件运算符的问题:
Why does this function return an lvalue reference given rvalue arguments?
Error: lvalue required in this simple C code? (Ternary with assignment?)
clang 在这里是正确的,旧的行为是无条件地将值转换为 prvalue,看起来 gcc 仍在实现。
这是 DR 1560 which was fixed by the resolution of DR 1550 的主题。 DR 1560 说:
A glvalue appearing as one operand of a conditional-expression in which the other operand is a throw-expression is converted to a prvalue, regardless of how the conditional-expression is used:
If either the second or the third operand has type void, then the lvalue-to-rvalue (7.1 [conv.lval]), array-to-pointer (7.2 [conv.array]), and function-to-pointer (7.3 [conv.func]) standard conversions are performed on the second and third operands, and one of the following shall hold:
- The second or the third operand (but not both) is a throw-expression (18.1 [except.throw]); the result is of the type of the other and is a prvalue.
这似乎是无端的和令人惊讶的。
和 DR 1550
将 [expr.cond] 中的措辞更改为我们现在的措辞:
The second or the third operand (but not both) is a (possibly parenthesized) throw-expression; the result is of the type and value category of the other. The conditional-expression is a bit-field if that operand is a bit-field.
所以看起来 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,它有以下简化的测试用例:
I wanted to bump this bug and supply a simpler test-case:
void blah(int&) {} int main() { int i{}; blah(true ? i : throw); }
result with 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); ~~~~~^~~~~~~~~~~~~