C++ 在C++;,如果throw是一个表达式,它的类型是什么?

C++ 在C++;,如果throw是一个表达式,它的类型是什么?,c++,throw,C++,Throw,我在一次对reddit的短暂尝试中发现了这个: 基本上,作者指出在C++中: throw "error" 这是一个表达。在C++的标准中,这在文本和语法中都是相当清楚的。然而,不清楚的是(至少对我来说)表达的类型是什么?我猜是“void”,但对g++4.4.0和Comeau进行了一点实验,得到了以下代码: void f() { } struct S {}; int main() { int x = 1; const char

我在一次对reddit的短暂尝试中发现了这个:

基本上,作者指出在C++中:

throw "error"
这是一个表达。在C++的标准中,这在文本和语法中都是相当清楚的。然而,不清楚的是(至少对我来说)表达的类型是什么?我猜是“
void
”,但对g++4.4.0和Comeau进行了一点实验,得到了以下代码:

    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
    }
编译器在//1上没有问题,但在//2上有问题,因为条件运算符中的类型不同。因此,
throw
表达式的类型似乎不是空的

那是什么呢

如果您回答,请引用标准中的话来支持您的陈述


事实证明,这与其说是关于抛出表达式的类型,不如说是关于条件运算符如何处理抛出表达式——我当然不知道这一点 在今天之前知道。感谢所有回复者,尤其是大卫·桑利。

“抛出表达式的类型为void”


ISO14882第15节根据标准,5.16第2段第一点,“第二个或第三个操作数(但不是两者)是一个抛出表达式(15.1);结果是另一个的类型,是一个右值。”因此,条件运算符不关心抛出表达式是什么类型,而只使用另一个类型

事实上,15.1第1段明确指出“抛出表达式的类型为void。”

来自[expr.cond.2](条件运算符
?:
):

如果第二个或第三个操作数的类型(可能是cv限定)为void,则将左值转换为右值, 数组到指针和函数到指针的标准转换在第二个和第二个 第三个操作数,且以下操作数之一应保持:

-第二个或第三个操作数(但不是两者)是一个抛出表达式; 结果是另一个的类型,是一个右值

-第二个和第三个操作数都具有void类型; 结果为void类型,为右值。 [注:这包括两个操作数都是抛出表达式的情况。-结束注]


因此,在第一种情况下,在
//1
中,您违反了“以下条件之一应成立”,因为在这种情况下,它们都不成立。

您可以拥有一台打印机:

模板
结构打印类型;
int main()
{
打印类型a;
}
基本上,
PrintType
缺少实现将导致编译错误报告显示:

未定义模板的隐式实例化
PrintType


因此,我们实际上可以验证
throw
表达式的类型是否为
void
(是的,其他答案中提到的标准引用验证了这不是特定于实现的结果-尽管gcc很难打印有价值的信息)

+1个可怕的问题。这是一个聪明的测试方法。这个链接似乎相当清楚地表明,类型是由编译器决定的,是它所需要的任何类型。我认为链接的文章在我看了之后已经更新了,我确信事实就是这样。然而,我在标准中找不到它。而且可能不是-双d=抛出“foo”;是g++=(没有用comeau测试过)+1的错误吗?我很想知道答案。那么g++和comeau都没有为我的//1案例给出错误吗?@Neil,不是真的,因为根据C++/5.16/2,条件运算符的第二个和第三个操作数可以是
void
OK类型-我认为我们有一个赢家。请注意,抛出表达式是赋值表达式。因此,对于大多数运算符来说,它们是一个语法错误。显然,可以将它们隐藏在括号中,但如果它们没有被忽略(例如,内置运算符的第一个参数),这是一个类型错误。真正让我吃惊的是,他们想到了这种情况,并做出了一些合理的事情。
template<typename T>
struct PrintType;

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