std :: throw_with_nested是否需要Exception虚拟基类的默认构造函数?

时间:2014-08-15 10:01:17

标签: c++ exception c++11 exception-handling libstdc++

为什么这不能编译(尝试使用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() {}this bug的解决方法。

2 个答案:

答案 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);

     

Uremove_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]

因此编译器正确拒绝了此代码