异常规范如何影响虚拟析构函数重写? C++标准对具有异常规范的虚函数陈述如下:
如果虚拟函数具有异常规范,则在任何派生类中重写该虚拟函数的任何函数的所有声明(包括定义)应仅允许基类虚拟函数的异常规范(C++03§15.4/3)允许的异常 因此,以下内容的格式不正确:异常规范如何影响虚拟析构函数重写? C++标准对具有异常规范的虚函数陈述如下:,c++,exception,exception-handling,destructor,exception-specification,C++,Exception,Exception Handling,Destructor,Exception Specification,如果虚拟函数具有异常规范,则在任何派生类中重写该虚拟函数的任何函数的所有声明(包括定义)应仅允许基类虚拟函数的异常规范(C++03§15.4/3)允许的异常 因此,以下内容的格式不正确: struct B { virtual void f() throw() { } // allows no exceptions }; struct D : B { virtual void f() { } // allows all exceptions }; (1) 这个规则
struct B {
virtual void f() throw() { } // allows no exceptions
};
struct D : B {
virtual void f() { } // allows all exceptions
};
(1) 这个规则适用于析构函数吗?也就是说,下面的格式是否正确
struct B {
virtual ~B() throw() { }
};
struct D : B {
virtual ~D() { }
};
(2) 该规则如何应用于隐式声明的析构函数?也就是说,下面的格式是否正确
struct B {
virtual ~B() throw() { }
};
struct D : B {
// ~D() implicitly declared
};
虽然在一般情况下应该这样做,但这个问题具有实际意义,因为std::exception
析构函数是虚拟的,并且有一个空的异常规范
由于不允许从析构函数抛出异常是一种良好的做法,为了简化任何示例,让我们假设析构函数要么允许所有异常(即,它没有异常规范),要么不允许任何异常(即,它有一个空的异常规范)。(1)这个规则适用于析构函数吗?
是的,此规则适用于析构函数(析构函数规则没有例外),因此此示例的格式不正确。为了使其格式良好,~D()
的异常规范必须与~B()
的异常规范兼容,例如
struct B {
virtual ~B() throw() { }
};
struct D : B {
virtual ~D() throw() { }
};
(2) 该规则如何应用于隐式声明的特殊成员函数?
C++标准对隐含声明的特殊成员函数如下:
隐式声明的特殊成员函数应具有异常规范 如果f
是隐式声明的默认构造函数、复制构造函数、析构函数或复制赋值运算符,则其隐式异常规范指定类型idT
,当且仅当由f
的隐式函数直接调用的函数的异常规范允许T
定义
如果直接调用的任何函数允许所有异常,f
应允许所有异常,如果直接调用的每个函数不允许任何异常,f
应不允许任何异常(C++03§15.4/13)
隐式声明的析构函数直接调用哪些函数
在执行析构函数的主体并销毁主体中分配的任何自动对象之后,类X
的析构函数调用
的直接成员的析构函数X
的直接基类的析构函数和X
- 如果
是派生最多的类的类型,则其析构函数调用X
的虚拟基类的析构函数X
struct B {
virtual ~B() throw() { }
};
struct D : B {
// ~D() implicitly declared
};
隐式声明的~D()
调用的唯一析构函数是~B()
。由于~B()
不允许异常,~D()
不允许异常,就好像它被声明为virtual~D()throw()
此异常规范显然与~B()
兼容,因此此示例格式良好
作为一个实际例子,为什么要考虑这一点,请考虑以下内容:
struct my_exception : std::exception {
std::string message_;
};
~string()
允许所有异常,因此隐式声明的~my_exception()
允许所有异常。基类析构函数~exception()
,是虚拟的,不允许任何异常,因此派生类析构函数与基类析构函数不兼容,这是格式错误的
为了使此示例格式良好,我们可以使用空异常规范显式声明析构函数:
struct my_exception : std::exception {
virtual ~my_exception() throw() { }
std::string message_;
};
虽然经验法则是永远不要编写异常规范,但至少有一种常见情况需要这样做。此外,从析构函数中抛出异常也不是一个好主意。@Ben Voigt:同意;我在问题的最后一句说了。我想我已经养成了在答案中寻找细节的习惯,而不是在问题中。只要回答者比提问者聪明,这可能没关系。这导致了一个相当奇怪的推论:你比自己聪明吗?在你发布问题的同时,你是如何得到如此详细的答案的?@Ben:我今天早上本来打算发布这个问题,但最后我自己研究了答案,因为我很感兴趣(我真的不太熟悉异常规范的细节),所以我决定先问,然后自己回答这个问题。我相当肯定,如果有什么不同的话,我比我自己还要笨:-)。我当然有兴趣看到这个问题的其他答案,特别是如果我错过了一些细节或一些密切相关的问题。我在研究GCC4.6中的“松散抛出说明符”错误时偶然发现了这个问题。与您的
struct D:B
示例相反,gcc的隐式析构函数似乎没有throw声明。