Why am I getting an “exception weaker than basic” error with this piece of code?

Trying to compile the following code using clang version in Xcode 6.1 (clang-600.0.54 based on LLVM 3.5svn), with -std=c++11 and -stdlib=libc++ gives me some errors that I really don't understand.

 #include <functional> struct Impl { typedef std::function<void ()> L; L l; int i; }; struct Hndl { Impl* impl; Hndl(Impl* i): impl(i) {} ~Hndl() noexcept(false) {} }; int main(int argc, char * argv[]) { Hndl h(new Impl()); h.impl->l = [=] { h.impl->i = 42; }; return 0; } 

Results:

 In file included from t.cpp:1: /Applications/Xcode-6.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:1293:52: error: exception specification of overriding function is more lax than base version template<class _FD, class _Alloc, class _FB> class __func; ^ /Applications/Xcode-6.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:1593:13: note: in instantiation of template class 'std::__1::__function::__func<<lambda at t.cpp:20:14>, std::__1::allocator<<lambda at t.cpp:20:14> >, void ()>' requested here if (sizeof(_FF) <= sizeof(__buf_) && is_nothrow_copy_constructible<_Fp>::value) ^ /Applications/Xcode-6.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:1697:5: note: in instantiation of function template specialization 'std::__1::function<void ()>::function<<lambda at t.cpp:20:14> >' requested here function(_VSTD::forward<_Fp>(__f)).swap(*this); ^ t.cpp:20:12: note: in instantiation of function template specialization 'std::__1::function<void ()>::operator=<<lambda at t.cpp:20:14> >' requested here h.impl->l = [=] ^ /Applications/Xcode-6.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:1281:39: note: overridden virtual function is here _LIBCPP_INLINE_VISIBILITY virtual ~__base() {} ^ 1 error generated. 

It seems that Impl::L::~L() somehow inherits noexcept(false) from Hndl::~Hndl() , but I don't know why. Interestingly, the same code compiles if I comment on the purpose of h.impl->i inside lambda. It also compiles if I noexcept(false) specification from Hndl::~Hndl() , but I need it (it would be a bit to explain why, but I do it). It also compiles if the lambda is fixed with ref, but the whole goal here would be to be able to copy descriptors that share the implementation. Adding noexcept(true) to Impl::~Impl() does not help.

ideone.com The C ++ 11 compiler happily compiles it as is.

Can someone explain to me what is going on here?

+5
source share
3 answers

A lambda captures h , which has a potentially throwing destructor, so the lambda closure type also has a potentially throwing destructor.

With this in mind, you can reduce the problem to this:

 #include <functional> struct F { void operator()() { } ~F() noexcept(false) {} }; int main() { std::function<void ()> f = F{}; } 

It appears that std::function in libC ++ cannot store the type being called without the nothrow destructor, which looks like an error in libC ++.

From the error messages, it seems that fixing can be as simple as adding an explicit noexcept to the __func destructor, but I am not familiar with the implementation, so it may not be so simple.

I do not see any obvious workarounds, except that you transfer your Hndl type to another type using the noexcept destructor, so grabbing it in lambda does not make the lambda does not have a noexcept(false) destructor. I tried something like this, but libC ++ seems to have similar problems in shared_ptr :

  std::function<void ()> f = std::bind(&Hndl::operator(), std::make_shared<Hndl>()); 
+3
source

I think the problem is that Hndl is fixed in lambda by value, and the std::function destructor is considered as noexcept(true) , since it does not indicate otherwise. Thus, an Hndl instance cannot be safely destroyed in the l destructor. As for why the error disappears if the member assignment is removed from the lambda - most likely, the captured value is simply optimized by the compiler.

+1
source

A workaround is possible here, based on http://www.codeproject.com/Articles/313312/Cplusplus-Lambda-Storage-Without-libcplusplus

Please note that the code below is simplified for demo purposes; it is incomplete and only processes void () lambdas. See the link above for the real thing (although in my particular case void () is enough).

 #include <functional> struct Lambda { void* lambda; void (*execute)(void*); template <typename T> Lambda& operator=(T&& t) { lambda = new T(t); execute = [](void* lambda) { ((T*)lambda)->operator()(); }; return *this; } void operator()() { execute(lambda); } }; // --- struct Impl { Lambda l; int i; }; struct Hndl { Impl* impl; Hndl(Impl* i): impl(i) {} ~Hndl() noexcept(false) {} }; int main(int argc, char * argv[]) { Hndl h(new Impl()); h.impl->l = [=] { h.impl->i = 42; }; h.impl->l(); return 0; } 
0
source

Source: https://habr.com/ru/post/1205946/


All Articles