std::throw_with_nested expects polymorphic type in C++11?
Asked Answered
W

3

12

Why does this not compile (tried with Clang 3.4.2 and GCC versions 4.7.4, 4.8.3 and 4.9.1):

#include <exception>

struct E { E(int) {} };

int main() {
  std::throw_with_nested(E(42));
  return 0;
}

Errors from 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 'static const std::nested_exception* std::__get_nested_helper<_Ex>::_S_get(const _Ex&) [with _Ex = E]':
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:104:51:   required from 'const std::nested_exception* std::__get_nested_exception(const _Ex&) [with _Ex = E]'
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:138:38:   required from 'void std::throw_with_nested(_Ex) [with _Ex = E]'
test.cpp:6:31:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:90:59: error: cannot dynamic_cast '& __ex' (of type 'const struct E*') to type 'const class std::nested_exception*' (source type is not polymorphic)
      { return dynamic_cast<const nested_exception*>(&__ex); }
                                                          ^

Errors from Clang 3.4.2:

In file included from test.cpp:1:
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/exception:163:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:90:16: error: 'E' is not polymorphic
      { return dynamic_cast<const nested_exception*>(&__ex); }
              ^                                     ~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:104:40: note: in instantiation of member function 'std::__get_nested_helper<E>::_S_get' requested here
    { return __get_nested_helper<_Ex>::_S_get(__ex); }
                                      ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.1/include/g++-v4/bits/nested_exception.h:138:11: note: in instantiation of function template specialization 'std::__get_nested_exception<E>' requested here
      if (__get_nested_exception(__ex))
          ^
test.cpp:6:8: note: in instantiation of function template specialization 'std::throw_with_nested<E>' requested here
  std::throw_with_nested(E(42));
      ^

Does std::throw_with_nested in C++11 expect an argument with polymorphic type or is this a bug in the compiler or libstdc++?

Waxy answered 15/8, 2014 at 9:38 Comment(1)
Looks like a bug. 18.8.6 requires the compiler to create a suitable polymorphic type.Equity
L
16

It's a bug.

When I implemented it for libstdc++ in 2009 the spec in N2619 required E to be a polymorphic type, but the final spec in the 2011 standard is different and the implementation in libstdc++ was never changed.

Lonesome answered 15/8, 2014 at 10:2 Comment(1)
The bug is fixed in the svn trunkLonesome
M
8

This would appear to be a bug.

The standard says of std::throw_with_nested:

[[noreturn]] template <class T> void throw_with_nested(T&& t);

Let U be remove_reference<T>::type.

Requires: U shall be CopyConstructible.

Throws: if U is a non-union class type not derived from nested_exception, an exception of unspecified type that is publicly derived from both U and nested_exception and constructed from std::forward<T>(t), otherwise std::forward<T>(t).

§18.8.6 [except.nested]

Mickel answered 15/8, 2014 at 9:44 Comment(1)
Yep, the commit logs say it's my fault, although I don't remember implementing that file!Lonesome
I
3

It does indeed look like a bug (see other answers), ref §18.8.6/7. If not already derived from std::nested_exception, an unspecified type is used that is derived from E and nested_exception.

As a proposed work around whilst it is being fixed, explicitly derive from std::nested_exception or implement the destructor as virtual;

#include <exception>

struct E : std::nested_exception { E(int) {} };
// Alternative workaround... virtual destructor
// struct E { E(int) {} virtual ~E() {} };

int main() {
 try {
  std::throw_with_nested(E(42));
  return 0;
 }
 catch (E&) {
 }
}

Sample here.

Inconsistency answered 15/8, 2014 at 10:2 Comment(1)
Or add a virtual destructorLonesome

© 2022 - 2024 — McMap. All rights reserved.