2014-10-31 7 views
5

Versuchen, den folgenden Code mit der Clang-Version in Xcode 6.1 (clang-600.0.54 basierend auf LLVM 3.5svn) zu kompilieren, mit -std=c++11 und -stdlib=libc++ gibt mir einige Fehler, die ich nicht wirklich verstehe.Warum bekomme ich "Exception spec is lax als base" Fehler mit diesem Stück Code?

#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; 
} 

Ergebnisse:

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. 

Sieht aus, als ob Impl::L::~L() irgendwie die noexcept(false) von Hndl::~Hndl() erbt, aber ich weiß nicht, warum. Interessanterweise kompiliert der gleiche Code, wenn ich die Zuordnung zu h.impl->i innerhalb des Lambda auskommentieren. Kompiliert auch, wenn ich die noexcept(false) Spezifikation von Hndl::~Hndl() entferne, aber ich brauche das (wäre ein bisschen lang zu erklären, warum, aber ich tue). Kompiliert auch, wenn das Lambda durch ref erfasst, aber der ganze Punkt hier wäre in der Lage sein, Griffe zu kopieren, die eine Implementierung teilen. Das Hinzufügen von noexcept(true) zu Impl::~Impl() hilft nicht.

Der C++ 11-Compiler von ideone.com kompiliert sie gerne so, wie sie ist.

Kann mir jemand erklären, was hier passiert?

Antwort

3

Das Lambda fängt h ein, das einen potentiell werfenden Destruktor hat, also hat der Schließungstyp des Lambda auch einen potentiell werfenden Destruktor.

, die mit im Auge, können Sie das Problem auf diese reduzieren:

#include <functional> 

struct F 
{ 
    void operator()() { } 

    ~F() noexcept(false) {} 
}; 

int main() { 
    std::function<void()> f = F{}; 
} 

Es scheint, dass die std::function in libC++ nicht aufrufbar Typ ohne nothrow destructor, die wie ein Fehler in libC++ sieht speichern kann.

Aus den Fehlermeldungen sieht es so aus, als ob das Update so einfach wäre wie das Hinzufügen eines expliziten noexcept zu dem Destruktor __func, aber ich bin nicht vertraut mit der Implementierung, so dass es nicht so einfach sein könnte.

Ich sehe keine offensichtlichen Abhilfen außer Ihren Hndl Typ in einer anderen Typ mit einem noexcept destructor Verpackung, so dass es in dem Lambda-Erfassung nicht das Lambda macht einen noexcept(false) destructor. Ich habe versucht, so etwas, aber libC++ scheint in shared_ptr ähnliche Probleme zu haben:

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

Ist das explizit im Standard erlaubt? [This] (http://en.cppreference.com/w/cpp/utility/functional/function/~function) Referenz besagt, dass "~ Funktion" hat "noexpect" Spezifizierer, aber es kann falsch sein (wieder). – dewaffled

+0

'std :: function' hat definitiv einen 'noexcept'-Destruktor, aber es sollte immer noch möglich sein, Typen zu speichern, die das nicht tun. Wenn dein Destruktor nicht wirklich wirft, gibt es kein Problem und das Programm sollte einwandfrei funktionieren. Wenn Ihr Destruktor bei der Speicherung in einer 'std :: function 'auslöst, würde dies zu einem Aufruf von' std :: terminate() ' –

+0

@imre führen. Daher funktioniert der Code nicht, wenn der Destruktor trotzdem einen Wurf ausführt. Ich sehe keinen Grund, es als "noexcept (false)" zu deklarieren. – dewaffled

1

Ich denke, das Problem ist, dass Hndl in Lambda von Wert erfaßt und std::function destructor als noexcept(true) behandelt, da sie sonst nicht in seiner Definition angegeben ist. So Hndl Instanz kann in l Destructor nicht sicher zerstört werden. Warum der Fehler verschwindet, wenn die Member-Zuweisung aus Lambda entfernt wird - der wahrscheinlichste Wert wird nur vom Compiler optimiert.

+0

Der erste Teil Ihrer Antwort ist richtig, der letzte Satz ist nicht. Der Fehler verschwindet, wenn Sie die Zuweisung entfernen, weil dann 'h' nicht erfasst wird, so dass die Ausnahmebestimmung von' ~ Hndl() 'keine Relevanz hat. –

0

Hier ist eine mögliche Abhilfe, basierend auf http://www.codeproject.com/Articles/313312/Cplusplus-Lambda-Storage-Without-libcplusplus

Beachten Sie, dass der folgende Code zu Demonstrationszwecken vereinfacht wird; Es ist unvollständig und behandelt nur void() lambdas. Siehe den obigen Link für die echte Sache (obwohl in meinem speziellen Fall void() genug ist).

#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; 
} 
Verwandte Themen