In C++, if throw is an expression, what is its type?

C++Throw

C++ Problem Overview


I picked this up in one of my brief forays to reddit:

<http://www.smallshire.org.uk/sufficientlysmall/2009/07/31/in-c-throw-is-an-expression/>

Basically, the author points out that in C++:

throw "error"

is an expression. This is actually fairly clearly spelt out in the C++ Standard, both in the main text and the grammar. However, what is not clear (to me at least) is what is the type of the expression? I guessed "void", but a bit of experimenting with g++ 4.4.0 and Comeau yielded this code:

    void f() {
    }
    
    struct S {};
    
    int main() {
    	int x = 1;
    	const char * p1 = x == 1 ? "foo" : throw S();  // 1
    	const char * p2 = x == 1 ? "foo" : f();        // 2
    }

The compilers had no problem with //1 but barfed on //2 because the the types in the conditional operator are different. So the type of a throw expression does not seem to be void.

So what is it?

If you answer, please back up your statements with quotes from the Standard.


This turned out not to be so much about the type of a throw expression as how the conditional operator deals with throw expressions - something I certainly didn't know about before today. Thanks to all who replied, but particularly to David Thornley.

C++ Solutions


Solution 1 - C++

According to the standard, 5.16 paragraph 2 first point, "The second or the third operand (but not both) is a throw-expression (15.1); the result is of the type of the other and is an rvalue." Therefore, the conditional operator doesn't care what type a throw-expression is, but will just use the other type.

In fact, 15.1, paragraph 1 says explicitly "A throw-expression is of type void."

Solution 2 - C++

>"A throw-expression is of type void"

ISO14882 Section 15

Solution 3 - C++

From [expr.cond.2] (conditional operator ?:):

> If either the second or the third operand has type (possibly cv-qualified) void, then the lvalue-to-rvalue, array-to-pointer, and function-to-pointer standard conversions are performed on the second and third operands, and one of the following shall hold:

> — The second or the third operand (but not both) is a throw-expression; > the result is of the type of the other and is an rvalue. > > — Both the second and the third operands have type void; > the result is of type void and is an rvalue. > [ Note: this includes the case where both operands are throw-expressions. — end note ]

So, with //1 you were in the first case, with //2, you were violating "one of the following shall hold", since none of them do, in that case.

Solution 4 - C++

You can have a type printer spit it out for you :

template<typename T>
struct PrintType;

int main()
{
	PrintType<decltype(throw "error")> a; 
}

Basically the lack of implementation for PrintType will cause the compilation error report to say :

>implicit instantiation of undefined template PrintType<void>

so we can actually verify that throw expressions are of type void (and yes, the Standard quotes mentioned in other answers verify that this isn't an implementation specific outcome - though gcc has a hard time printing valuable info)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanonView Question on Stackoverflow
Solution 1 - C++David ThornleyView Answer on Stackoverflow
Solution 2 - C++DraemonView Answer on Stackoverflow
Solution 3 - C++Marc Mutz - mmutzView Answer on Stackoverflow
Solution 4 - C++Nikos AthanasiouView Answer on Stackoverflow