首页 > 解决方案 > “throw MyException()”和“throw (MyException())”有区别吗?

问题描述

我想知道编写异常收件箱和发件箱是否会改变特定程序的行为,例如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;
}

标签: c++exception

解决方案


异常对象是复制初始化的(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 之前的可选省略(例如-fno-elide-constructors在 GCC 中)。

如果像其他一些人声称的那样,可能存在“抛出引用”(变得悬空)这样的事情,那么您只会看到T. 事实上,没有引用类型的表达式这种东西,尽管尽了最大的努力decltype并向auto你假装有。

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

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


推荐阅读