It is well known that throw
can be placed as the second or the third operand of C++ ternary operator ?:
. 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
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.
According to https://en.cppreference.com/w/cpp/language/operator_other, it seems that GCC/Clang are right since
Either E2 or E3 (but not both) is a (possibly parenthesized) throw-expression.
and here we have parenthesized comma expression just finishing with throw-expression.
According to the standard, is MSVC incorrect in accepting the last line of the example?
Clang and GCC are correct to reject it. It's pretty straightforward:
[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.
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. 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.