简体   繁体   English

`throw` 可以在 C++ 条件(三元)运算符内的逗号子表达式内吗?

[英]Can `throw` be inside a comma subexpression within C++ conditional (ternary) operator?

It is well known that throw can be placed as the second or the third operand of C++ ternary operator ?: .众所周知, throw可以作为 C++ 三元运算符?:的第二个或第三个操作数。 But can it be inside a comma subexpression of there operands?但是它可以在操作数的逗号子表达式中吗? It looks like compilers diverge in this regard.看起来编译器在这方面存在分歧。 Please consider an example:请考虑一个例子:

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

This example is accepted by MSVC, but rejected by both GCC and Clang, demo: https://gcc.godbolt.org/z/6q46j5exP这个例子被MSVC接受,但是被GCC和Clang都拒绝了,demo: https://gcc.godbolt.org/z/6q46j5exP

Though the error message:虽然错误信息:

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);
      |             ^

suggests that it was not rejected intentionally but rather the compiler thinks that the third operand not only has formal type void but actually can return.表明它不是有意拒绝的,而是编译器认为第三个操作数不仅具有正式类型void ,而且实际上可以返回。

According to https://en.cppreference.com/w/cpp/language/operator_other , it seems that GCC/Clang are right since根据https://en.cppreference.com/w/cpp/language/operator_other ,似乎 GCC/Clang 是正确的,因为

Either E2 or E3 (but not both) is a (possibly parenthesized) throw-expression. E2 或 E3(但不是两者)是一个(可能带括号的)抛出表达式。

and here we have parenthesized comma expression just finishing with throw-expression.在这里我们用括号括起来的逗号表达式以 throw-expression 结尾。

According to the standard, is MSVC incorrect in accepting the last line of the example?根据标准,MSVC 在接受示例的最后一行时是否不正确?

Clang and GCC are correct to reject it. Clang 和 GCC 拒绝它是正确的。 It's pretty straightforward:这很简单:

[expr.cond] [表达式条件]

2 If either the second or the third operand has type void , one of the following shall hold: 2如果第二个或第三个操作数的类型为void ,则应满足以下条件之一:

  • The second or the third operand (but not both) is a (possibly parenthesized) throw-expression ( [expr.throw] );第二个或第三个操作数(但不是两者)是一个(可能带括号的)throw 表达式( [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 ;第二个和第三个操作数的类型都是void the result is of type void and is a prvalue.结果的类型为void ,并且是纯右值。

The wording is pretty precise here.这里的措辞非常准确。 It says one operand is a throw-expression when the first bullet applies.它说当第一个项目符号适用时,一个操作数是一个抛出表达式。 And (std::cout << "smth\n", throw 0) is not a throw-expression.并且(std::cout << "smth\n", throw 0)不是抛出表达式。 It's parenthesized comma expression.它是带括号的逗号表达式。

So we can only be in the case of the second bullet, but its conditions don't hold either.所以我们只能在第二颗子弹的情况下,但它的条件也不成立。 So a "shall" requirement is broken, and the program is thus ill-formed.所以“应该”的要求被打破了,程序因此是病式的。

Now, MSVC may be offering an extension around this, but it's not standard.现在,MSVC 可能会为此提供扩展,但它不是标准的。

As noted by @StoryTeller - Unslander Monica , there is a specification restriction with operands evaluating to void .正如@StoryTeller-Unslander Monica所指出的,对计算为void的操作数存在规范限制。

I should note, however, that in this case the restriction is trivially bypassed:然而,我应该注意到,在这种情况下,限制被简单地绕过了:

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

The extra , 2 changes the type of (std::cout << "smth\n", throw 0, 2) to int , at which point the quoted rule no longer applies -- the operand is no longer of type void -- and therefore there is no longer any restriction on the operand. extra , 2(std::cout << "smth\n", throw 0, 2)的类型更改为int ,此时引用的规则不再适用——操作数不再是void类型——因此对操作数不再有任何限制。

(And now we can question what is the point of the restriction in the standard...) (现在我们可以质疑标准中限制的意义是什么......)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM