"throw MyException()"和"throw (MyException())"有区别吗?

Is there a difference between "throw MyException()" and "throw (MyException())"?

本文关键字:MyException throw 有区别      更新时间:2024-09-22

我想知道在收件箱和收件箱外写入异常是否会改变特定程序的行为,例如throw MyException((抛出(MyException(((

我的代码:

#include <iostream>
#include <exception>
using namespace std;
class MyException: public exception {
public:
virtual const char* what() const throw()
{
return "Something bad happened";
}
};
class Test
{
public:
void goWrong()
{
throw (MyException());
}
};
int main()
{
Test test;
try
{
test.goWrong();
}
catch (MyException &err)
{
cout << "The Exception is Executed: " << err.what() << 'n';
}
cout << "Still Running" << 'n';
return 0;
}

异常对象是复制初始化的(except.throw/3(,所以使用哪一个并不重要;结果是一样的。

复制初始化将忽略引用限定符,即使您从中得到了一个。

我们可以用一些跟踪输出来证明这一点:

#include <cstdio>
using std::printf;
struct T
{
T() { printf("T()n"); }
~T() { printf("~T()n"); }
T(const T&) { printf("T(const T&)n"); }
T(T&&) { printf("T(T&&)n"); }
T& operator=(const T&) { printf("T& operator=(const T&)n"); return *this; }
T& operator=(const T&&) { printf("T& operator=(T&&)n"); return *this; }
};
int main()
{
try
{
throw T();
}
catch (const T&) {}
}

即使从throw T()切换到throw (T()),语义(和输出(也完全相同:

T()
T(T&&)
~T()
~T()

也就是说,构建一个临时的T(),然后将其移动到真正的异常对象(存在于某个神奇的"安全空间"中(,最终两者都被销毁。

请注意,要查看此证明,您必须返回到C++14(因为C++17在需要真正的异常对象之前,根据所谓的"强制省略",并关闭C++17之前的可选省略(例如GCC中的-fno-elide-constructors(

如果,正如其他一些人所声称的那样,可能会有这样一件事:;抛出引用";(它变得摇摇欲坠(,你只会看到T的一个构造。事实上,不存在引用类型的表达式,尽管decltypeauto尽了最大努力向你假装存在。

在这两种情况下,我们给throw的表达式都是右值MyException

当使用推导的返回类型(通过decltype(auto)(时,我们必须小心return的原因是推导考虑了值类别。如果你有一个局部变量int x,那么在return x中,表达式xxvalueint,所以你推导的类型将是int,一切都很好。如果您改为编写return (x),则表达式为左值int,这将导致推导出的int&类型,并且突然出现问题。请注意,这两个表达式都没有引用类型(实际上并不存在(。但无论如何,这些都与throw场景无关,尤其是因为你的论点最初是暂时的。

我相信它的工作原理类似于C++14及更高版本中的返回语句,括号中表示要抛出的类型是引用,而不是异常实例本身。引用将被存储、复制并像处理异常一样处理,但引用指向的临时对象可能会在堆栈展开过程中被销毁,这可能会导致问题。如果您的异常对象是POD类型,则可能不会立即引起问题,因为在堆栈上创建的此类对象的销毁是微不足道的,但它仍然是未定义的行为。