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?
The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark ( ? ), then an expression to execute if the condition is truthy followed by a colon ( : ), and finally the expression to execute if the condition is falsy.
For example, you should not try to make ternary operators nested inside of ternary operators. Although writing code this way works correctly in JavaScript, it is very hard to read and understand.
We use the ternary operator in C to run one code when the condition is true and another code when the condition is false. For example, (age >= 18) ? printf("Can Vote") : printf("Cannot Vote");
In the above syntax, we have tested 2 conditions in a single statement using the ternary operator. In the syntax, if condition1 is incorrect then Expression3 will be executed else if condition1 is correct then the output depends on the condition2. If condition2 is correct, then the output is Expression1.
The ternary operator is a shorthand way to express an if-else statement. Many C programmers use the ternary operator in place of if-else conditional statements. The conditional operator takes three operands, that is why it is also called the ternary operator.
The ternary operator take three arguments: 1 The first is a comparison argument 2 The second is the result upon a true comparison 3 The third is the result upon a false comparison
The conditional operator is kind of similar to the if-else statement as it does follow the same algorithm as of if-else statement but the conditional operator takes less space and helps to write the if-else statements in the shortest way possible. Syntax: The conditional operator is of the form. variable = Expression1 ? Expression2 : Expression3.
Ternary operators can be nested just like if-else statements. Consider the following code: If this article was helpful, tweet it. Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers.
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.
As noted by @StoryTeller - Unslander Monica, there is a specification restriction with operands evaluating to 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.
(And now we can question what is the point of the restriction in the standard...)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With