Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?:. 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?

like image 768
Fedor Avatar asked Aug 14 '21 08:08

Fedor


People also ask

How do you handle 3 conditions in a ternary operator?

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.

Can we use ternary operator inside ternary operator?

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.

Which is the conditional operator ternary operator used in C?

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

Can ternary operator have two conditions?

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.

What is the ternary operator in C?

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.

How many arguments does the ternary operator take?

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

What is the conditional operator in C++?

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.

Can I use a ternary operator in a nested statement?

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.


Video Answer


2 Answers

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 type void 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.

like image 75
StoryTeller - Unslander Monica Avatar answered Oct 16 '22 16:10

StoryTeller - Unslander Monica


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...)

like image 23
Matthieu M. Avatar answered Oct 16 '22 16:10

Matthieu M.