投掷一个右值

Bar*_*rry 13 c++ throw language-lawyer c++14

考虑片段:

try {
    Foo f;
    throw std::move(f);
}
catch (Foo& f) { }
Run Code Online (Sandbox Code Playgroud)

[expr.throw]说:

异常对象的类型是通过从操作数的静态类型中删除任何顶级cv限定符并将类型从"T的数组"或"返回T的函数"调整为"指向T的指针"或"指针"来确定的.函数返回T",分别.

这将是Foo&&.然后根据[except.throw]初始化异常对象:

抛出异常copy-initializes(8.5,12.8)一个临时对象,称为异常对象.临时是一个左值,用于初始化匹配处理程序中声明的变量(15.3).如果异常对象的类型是不完整类型或指向不完整类型的指针(可能是cv-qualified),void则程序格式不正确.

这告诉我异常对象被初始化为:

Foo&& __exception_object = std::move(f);
Run Code Online (Sandbox Code Playgroud)

并且处理程序不匹配.但是,gcc和clang都会捕获此异常.那么这里的异常对象的实际类型是什么?如果Foo,为什么?

Jon*_*ely 14

表达式的静态类型永远不是引用类型.

1.3.24 [defns.static.type]定义"静态类型":

在不考虑执行语义的情况下对程序进行分析得到的表达式(3.9)的类型

"分析程序"的第一步是删除引用,参见5 [expr] p5,表达式可以有引用类型

如果表达式最初具有"引用T" 类型(8.3.2,8.5.3),则T在进行任何进一步分析之前调整类型.表达式指定由引用表示的对象或函数,表达式是左值或x值,具体取决于表达式.

std::move(f)xvalue表达式也是如此,具有静态类型Foo.

你不需要让rvalues来证明这一点,在C++ 03中也是如此:

int& f();
throw f();
Run Code Online (Sandbox Code Playgroud)

这引发了int不是int&.

在不考虑细节的情况下,异常对象是对象,而引用不是对象,因此异常对象不能作为引用.它必须是一个对象.