为什么这不能编译(尝试使用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 ++错误,还是我做错了什么?
答案 0 :(得分:2)
问题是当你有虚拟继承时,最后一个派生类负责构造基础。
在这种情况下,库正在尝试创建更多来自您的类,然后无法构造它们。
当您删除&#34;虚拟&#34;继承,最终的课程可以从你的课程中获得,没有问题。
实施&#34;代码&#34;正在点击错误消息......
template<typename _Except>
struct _Nested_exception : public _Except, public nested_exception
{
explicit _Nested_exception(_Except&& __ex)
: _Except(static_cast<_Except&&>(__ex))
{ }
};
所以它需要在它的构造函数中构造一个Base
,因为它现在是派生类最多的类,并且不能正确地执行dp。
答案 1 :(得分:1)
标准规定:
对于指定为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
类,并尝试使用您的副本或移动构造函数初始化它们,这两者都是隐式定义的。
在非虚拟继承的情况下,这很好,因为基类被隐式复制/移动构造函数递归复制或移动,如标准中所示:
非联合类
X
的隐式定义的复制/移动构造函数执行其基础和成员的成员复制/移动。§12.8[class.copy]
但是,由于虚拟继承和§10.1[class.mi](上面引用),基类(在这种情况下_Nested_exception
)负责初始化Base
,<强烈>不您Derived
课程的责任。
因此,尝试找到一个失败的默认构造函数,因为它被隐式删除,因为有另一个用户提供的构造函数,并且标准状态:
如果类X没有用户声明的构造函数,则没有参数的构造函数被隐式声明为默认值。
§12.1[class.ctor]
因此编译器正确拒绝了此代码。