5

尝试使用 Xcode 6.1 中的 clang 版本(基于 LLVM 3.5svn 的 clang-600.0.54)编译以下代码,-std=c++11-stdlib=libc++给出了一些我不太理解的错误。

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

结果:

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.

看起来好像Impl::L::~L()以某种方式继承了noexcept(false)of Hndl::~Hndl(),但我不知道为什么。有趣的是,如果我注释掉h.impl->ilambda 内部的赋值,同样的代码也会编译。如果我从中删除noexcept(false)规范,也会编译Hndl::~Hndl(),但我需要它(解释原因会有点长,但我这样做了)。如果 lambda 通过 ref 捕获,也会编译,但这里的重点是能够复制共享实现的句柄。添加noexcept(true)Impl::~Impl()没有帮助。

ideone.com 的 c++11 编译器很高兴地按原样编译它。

谁能解释一下这里发生了什么?

4

3 回答 3

3

lambda capturesh有一个潜在抛出的析构函数,因此 lambda 的闭包类型也有一个潜在抛出的析构函数。

考虑到这一点,您可以将问题简化为:

#include <functional>

struct F
{
    void operator()() { }

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

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

似乎std::functionlibc++ 中的 in 无法存储没有 nothrow 析构函数的可调用类型,这看起来像 libc++ 中的错误。

从错误消息看来,修复可能就像向析构函数添加显式一样简单noexcept__func但我不熟悉实现,所以可能没那么容易。

Hndl除了使用析构函数将您的类型包装在另一种类型中之外,我没有看到任何明显的解决方法noexcept,因此在 lambda 中捕获它不会使 lambda 具有noexcept(false)析构函数。我尝试过这样的事情,但 libc++ 似乎有类似的问题shared_ptr

    std::function<void ()> f = std::bind(&Hndl::operator(), std::make_shared<Hndl>());
于 2014-10-31T12:54:17.770 回答
1

我认为问题在于它Hndl是在 lambda 中按值捕获的,而std::function析构函数被视为noexcept(true)因为它的定义中没有另外说明。所以Hndl实例不能在析构函数中安全地l销毁。至于如果从 lambda 中删除成员分配,为什么错误会消失 - 很可能捕获的值只是被编译器优化掉了。

于 2014-10-31T12:28:04.213 回答
0

这是一个可能的解决方法,基于http://www.codeproject.com/Articles/313312/Cplusplus-Lambda-Storage-Without-libcplusplus

请注意,出于演示目的,以下代码已简化;它是不完整的,只处理 void() lambdas。请参阅上面的链接以了解真实情况(尽管在我的特定情况下 void() 就足够了)。

#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;
}
于 2014-10-31T14:35:26.997 回答