std::throw_with_nested期望Exception的虚基类的默认构造函数
std::throw_with_nested expects default constructor for virtual base class of Exception?
为什么不能编译(在Clang 3.4.2和GCC 4.7.4, 4.8.3和4.9.1版本中尝试过):
#include <exception>
struct Base {
inline Base(int) {}
virtual void f() {}
};
struct Derived: virtual Base {
inline Derived() : Base(42) {}
};
int main() {
std::throw_with_nested(Derived());
return 0;
}
GCC 4.9.1错误:
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/exception:163:0,
from test.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h: In instantiation of 'std::_Nested_exception<_Except>::_Nested_exception(_Except&&) [with _Except = Derived]':
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:126:60: required from 'void std::__throw_with_nested(_Ex&&, ...) [with _Ex = Derived]'
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:140:58: required from 'void std::throw_with_nested(_Ex) [with _Ex = Derived]'
test.cpp:13:35: required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:81:45: error: no matching function for call to 'Base::Base()'
: _Except(static_cast<_Except&&>(__ex))
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:81:45: note: candidates are:
test.cpp:4:10: note: Base::Base(int)
inline Base(int) {}
^
test.cpp:4:10: note: candidate expects 1 argument, 0 provided
test.cpp:3:8: note: constexpr Base::Base(const Base&)
struct Base {
^
test.cpp:3:8: note: candidate expects 1 argument, 0 provided
test.cpp:3:8: note: constexpr Base::Base(Base&&)
test.cpp:3:8: note: candidate expects 1 argument, 0 provided
如果省略virtual
关键字,则不会出现错误。这是一个GCC和Clang或libstd++错误,还是我做错了什么?
PS:请注意,virtual void f() {}
是这个错误的解决方案。
问题是,当您使用虚拟继承时,最后一个派生类负责构造基类。
在这种情况下,库试图创建更多从您的类派生的类,然后无法构造它们。
当你删除"虚拟"继承时,最终的类可以毫无问题地从你的类派生出来。
打出错误信息的实现"代码"…
template<typename _Except>
struct _Nested_exception : public _Except, public nested_exception
{
explicit _Nested_exception(_Except&& __ex)
: _Except(static_cast<_Except&&>(__ex))
{ }
};
所以它需要在它的构造函数中构造一个Base
,因为它现在是最派生的类,并且不能正确地dp
标准声明:
对于指定为virtual的每个不同基类,最派生的对象必须包含该类型的单个基类子对象。
§10.1 [class.mi]
对于std::throw_with_nested
:
[[noreturn]] template <class T> void throw_with_nested(T&& t);
设
U
为remove_reference<T>::type
要求:
U
应为CopyConstructible
。抛出:如果
U
不是从nested_exception
派生的非联合类类型,则抛出一个未指定类型的异常,该异常从U
和nested_exception
公开派生,并从std::forward<T>(t)
构造,否则为std::forward<T>(t)
。§18.8.6 [except.nested]
如果查看bits/nested_exception.h
,您将发现以下内容,这是库在libstdc++中为std::throw_with_nested
创建的基类:
template<typename _Except>
struct _Nested_exception : public _Except, public nested_exception
{
explicit _Nested_exception(_Except&& __ex)
: _Except(static_cast<_Except&&>(__ex))
{ }
};
这是从你的Derived
类派生出来的,并试图用你的copy或move构造函数初始化自己,这两个构造函数都是隐式定义的。
在非虚继承的情况下,这是可以的,因为基类是通过隐式复制/移动构造函数递归地复制或移动的,如标准中所述:
非联合类
X
的隐式定义的复制/移动构造函数对其基类和成员执行按成员复制/移动。§12.8 [class.copy]
然而,由于虚继承,和§10.1[类。(上面引用的),它是基类的责任(在本例中是_Nested_exception
初始化Base
, 而不是你的Derived
类的责任。
如果类X没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认值。
§12.1 [class.ctor]
因此这段代码被编译器正确拒绝
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 我什么时候会默认(而不是删除)基类中的复制和移动操作
- 为什么为派生类定义复制构造函数需要定义基类的默认构造函数?
- 默认基类初始值设定项
- 派生类是否具有基类的默认构造函数
- 具有虚拟析构函数的基类子类中的默认析构函数
- 如何防止在基类初始化器中调用默认构造函数?
- C++具有默认参数的基类构造函数
- 在调用基类构造函数时声明默认构造函数
- 重载方法的类接受模板和基类 - 如何默认某个方法
- 重载基类中的方法,默认为成员变量
- 默认赋值运算符有权访问基类的私有成员
- 为什么要删除基类的默认复制并移动ctor和赋值
- 正向声明要在基类中使用的派生类会导致缺少默认构造函数错误
- `使用`keyword表示基类中的默认行为
- 基类在派生类构造函数初始化列表时没有默认构造函数
- 尝试制作派生类,编译器需要抽象基的默认构造函数
- 警告:X的默认移动分配运算符将多次移动分配虚拟基类Y
- 虚拟继承是否强制基类默认为可构造的
- 为什么在虚拟基多重继承中只调用基类默认构造函数