让我说我有这个例外类:
struct MyException : public std::exception
{
MyException(const std::exception &exc) : std::exception(exc)
{
cout << "lval\n";
}
MyException(std::exception &&exc) : std::exception(std::forward<std::exception>(exc))
{
cout << "rval\n";
}
};
...
...
try
{
throw std::exception("Oh no!");
// above is rvalue since it's got no name, what if the throw is made as
// std::exception lvalExc("Oh wierd!");
// throw lvalExc;
// if the throw is made thus, how can it be caught by catch(std::exception &&exc)?
}
catch(std::exception &&rValRef)
{
cout << "rValRef!\n";
throw MyException(std::forward<std::exception>(rValRef));
}
当我试图通过值或( const )lvalue ref捕获时。编译器说这些情况已经由rvalue ref catch
子句处理,这是可以理解的,因为例外是xvalue,也许捕获xvalue的最好方法是rvalue ref(如果我错了)。但有人可以解释上述异常创建案例中的perfect forwarding吗?这是对的吗?即使它编译,它是有意义的还是有用的?我使用的C ++库是否应该为其std::exception
实现一个移动构造函数,以使这种用法真正有意义?我尝试在关于异常的rvalue引用上搜索文章和SO问题,找不到任何。
答案 0 :(得分:7)
实际上,异常处理对左值和右值有特殊规则。临时异常对象是左值,参见当前草案的15.1 / 3:
throw-expression初始化一个临时对象,称为异常对象,其类型是通过从throw的操作数的静态类型中删除任何顶级cv限定符并从“T的数组”调整类型来确定的。 “或”函数分别返回T“到”指向T“或”指向函数返回T的指针“。 临时值是左值,用于初始化匹配处理程序(15.3)中指定的变量。如果异常对象的类型是不完整类型或指向不完整类型的指针(可能是cv-qualified),则程序格式不正确。除了这些限制和15.3中提到的类型匹配的限制外,throw的操作数被完全视为调用(5.2.2)中的函数参数或return语句的操作数。
使用右值参考也是非法的,见15.3 / 1:
处理程序中的异常声明描述了可以导致输入该处理程序的异常类型。 异常声明不得表示不完整类型或右值引用类型。异常声明不应表示指向不完整类型的指针或引用,而不是void *,const void *,volatile void *或const volatile void *。
另外,您似乎并不了解完美转发。您的前向调用并不比移动更好。完美转发的想法是将参数的值类别编码为类型的一部分,并让模板参数推导计算出来。但是您的异常处理程序不是也不能是函数模板。
基本上,完美转发依赖于模板参数推导和右值引用:
void inner(const int&); // #1 takes only lvalues or const rvalues
void inner(int&&); // #2 takes non-const rvalues only
template<class T>
void outer(T && x) {
inner(forward<T>(x));
}
int main() {
int k = 23;
outer(k); // outer<T=int&> --> forward<int&> --> #1
outer(k+2); // outer<T=int> --> forward<int> --> #2
}
根据参数的值类别,模板参数推导推导将T推导为左值引用或正常值类型。由于参考崩溃,T&amp;&amp;在第一种情况下也是左值引用,在第二种情况下也是 rvalue 引用。如果你看到T&amp;&amp;和T是一个可以推导出的模板参数,它基本上是“捕捉一切”。 std :: forward恢复原始值类别(用T编码),这样我们就可以完美地将参数转发给重载的内部函数并选择正确的函数。但这只能起作用,因为外部是一个模板,因为有一个特殊的规则来确定T的价值类别。如果使用没有模板/模板参数推导的rvalue引用(如#2中),该函数将只接受rvalues。