`throw` 可以在 C++ 条件(三元)运算符中的逗号子表达式内吗?
Can `throw` be inside a comma subexpression within C++ conditional (ternary) operator?
众所周知,throw
可以作为C++三元运算符?:
的第二个或第三个操作数。但是它可以在操作数的逗号子表达式中吗?看起来编译器在这方面存在分歧。请考虑一个例子:
#include <iostream>
void foo(bool b) {
int i = b ? 1 : (throw 0); //ok everywhere
if ( !b )
(std::cout << "smth\n", throw 0); //ok everywhere
i = b ? 2 : (std::cout << "smth\n", throw 0); //ok in MSVC only
};
此示例被MSVC接受,但被GCC和Clang拒绝,演示:https://gcc.godbolt.org/z/6q46j5exP
虽然报错信息:
error: third operand to the conditional operator is of type 'void', but the second operand is neither a throw-expression nor of type 'void'
7 | i = b ? 2 : (std::cout << "smth\n", throw 0);
| ^
表明它不是有意拒绝的,而是编译器认为第三个操作数不仅具有正式类型 void
,而且实际上可以 return.
根据 https://en.cppreference.com/w/cpp/language/operator_other,似乎 GCC/Clang 是正确的,因为
Either E2 or E3 (but not both) is a (possibly parenthesized) throw-expression.
这里我们有带括号的逗号表达式,以 throw-expression 结尾。
根据标准,MSVC在接受示例的最后一行时是否错误?
Clang 和 GCC 拒绝它是正确的。非常简单:
[expr.cond]
2 If either the second or the third operand has type void
, one of the following shall hold:
- The second or the third operand (but not both) is a (possibly parenthesized) throw-expression ([expr.throw]); 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.
- Both the second and the third operands have type
void
; the result is of type void
and is a prvalue.
这里的措辞非常准确。当第一个项目符号适用时,它说一个操作数 是一个 抛出表达式。 (std::cout << "smth\n", throw 0)
不是 抛出表达式。它是带括号的逗号表达式。
所以只能是第二颗子弹的情况,但是它的条件也不成立。所以“应该”的要求被打破了,程序因此是病式的。
现在,MSVC 可能会为此提供扩展,但它不是标准的。
正如 所指出的,存在对计算为 void
的操作数的规范限制。
然而,我应该注意,在这种情况下,限制被轻而易举地绕过了:
#include <iostream>
void foo(bool b) {
int i = b ? 1 : (throw 0); //ok everywhere
if ( !b )
(std::cout << "smth\n", throw 0);
// OR
i = b ? 2 : throw ((std::cout << "smth\n"), 0);
// OR
i = b ? 2 : (std::cout << "smth\n", throw 0, 2);
// ^^^ extra addition
}
额外的 , 2
将 (std::cout << "smth\n", throw 0, 2)
的类型更改为 int
,此时引用的规则不再适用——操作数不再是 [=11= 类型] -- 因此对操作数不再有任何限制。
(现在我们可以质疑标准中限制的意义是什么...)
众所周知,throw
可以作为C++三元运算符?:
的第二个或第三个操作数。但是它可以在操作数的逗号子表达式中吗?看起来编译器在这方面存在分歧。请考虑一个例子:
#include <iostream>
void foo(bool b) {
int i = b ? 1 : (throw 0); //ok everywhere
if ( !b )
(std::cout << "smth\n", throw 0); //ok everywhere
i = b ? 2 : (std::cout << "smth\n", throw 0); //ok in MSVC only
};
此示例被MSVC接受,但被GCC和Clang拒绝,演示:https://gcc.godbolt.org/z/6q46j5exP
虽然报错信息:
error: third operand to the conditional operator is of type 'void', but the second operand is neither a throw-expression nor of type 'void'
7 | i = b ? 2 : (std::cout << "smth\n", throw 0);
| ^
表明它不是有意拒绝的,而是编译器认为第三个操作数不仅具有正式类型 void
,而且实际上可以 return.
根据 https://en.cppreference.com/w/cpp/language/operator_other,似乎 GCC/Clang 是正确的,因为
Either E2 or E3 (but not both) is a (possibly parenthesized) throw-expression.
这里我们有带括号的逗号表达式,以 throw-expression 结尾。
根据标准,MSVC在接受示例的最后一行时是否错误?
Clang 和 GCC 拒绝它是正确的。非常简单:
[expr.cond]
2 If either the second or the third operand has type
void
, one of the following shall hold:
- The second or the third operand (but not both) is a (possibly parenthesized) throw-expression ([expr.throw]); 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.
- Both the second and the third operands have type
void
; the result is of typevoid
and is a prvalue.
这里的措辞非常准确。当第一个项目符号适用时,它说一个操作数 是一个 抛出表达式。 (std::cout << "smth\n", throw 0)
不是 抛出表达式。它是带括号的逗号表达式。
所以只能是第二颗子弹的情况,但是它的条件也不成立。所以“应该”的要求被打破了,程序因此是病式的。
现在,MSVC 可能会为此提供扩展,但它不是标准的。
正如 void
的操作数的规范限制。
然而,我应该注意,在这种情况下,限制被轻而易举地绕过了:
#include <iostream>
void foo(bool b) {
int i = b ? 1 : (throw 0); //ok everywhere
if ( !b )
(std::cout << "smth\n", throw 0);
// OR
i = b ? 2 : throw ((std::cout << "smth\n"), 0);
// OR
i = b ? 2 : (std::cout << "smth\n", throw 0, 2);
// ^^^ extra addition
}
额外的 , 2
将 (std::cout << "smth\n", throw 0, 2)
的类型更改为 int
,此时引用的规则不再适用——操作数不再是 [=11= 类型] -- 因此对操作数不再有任何限制。
(现在我们可以质疑标准中限制的意义是什么...)