抛出右值

Throwing an rvalue

考虑片段:

try {
    Foo f;
    throw std::move(f);
}
catch (Foo& f) { }

[expr.throw] 表示:

the type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T”, respectively.

Foo&&。然后根据 [except.throw]:

初始化异常对象

Throwing an exception copy-initializes (8.5, 12.8) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable declared in the matching handler (15.3). If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed.

这向我暗示异常对象被初始化为:

Foo&& __exception_object = std::move(f);

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

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

1.3.24 [defns.static.type] 定义 "static type":

type of an expression (3.9) resulting from analysis of the program without considering execution semantics

"analysis of the program" 的第一步是删除引用,请参阅 5 [expr] p5 和 Expressions can have reference type

If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.

所以std::move(f)是一个xvalue表达式,静态类型Foo

您不需要涉及右值来证明这一点,在 C++03 中也是如此:

int& f();
throw f();

这会抛出 int 而不是 int&

不考虑具体情况,异常对象是对象,引用不是对象,所以异常对象不能是引用。它必须是一个对象。