具体使用C由它的类型++拉姆达(Instantiating C++ lambda by its ty

2019-06-25 13:14发布

我想一个方法,使仿函数的功能。 现在,我试图通过lambda函数来包装函数调用和以后实例化。 但是编译器说不是拉姆达构造被删除。 那么,有什么办法可以编译这段代码? 或者,也许另一种方式是什么?

#include <iostream>  

void func()
{
    std::cout << "Hello";
}

auto t = []{ func(); };
typedef decltype(t) functor_type;

template <class F>
void functor_caller()
{
    F f;
    f();
}

int main()
{
    functor_caller<functor_type>();
    return 0;
}

现在我得到这样的编译器错误:

error: use of deleted function  '<lambda()>::<lambda>()'

error: a lambda closure type has a deleted default constructor

在我看来,唯一的办法是使用宏:

#define WRAP_FUNC(f) \
struct f##_functor       \
{                       \
    template <class... Args >                             \
    auto operator()(Args ... args) ->decltype(f(args...)) \
    {                                                     \
        return f(args...);                                \
    }                                                     \
};

然后

WRAP_FUNC(func);

然后(在主)

functor_caller<func_functor>()

Answer 1:

该代码是没有意义的。 想象一下,你有这样的捕获的λ:

{
    int n = 0;
    auto t = [&n](int a) -> int { return n += a; };
}

还有什么比这可能意味着以缺省方式构造类型的对象decltype(t)

作为@Matthieu建议,你可以换拉姆达成function对象:

std::function<int(int)> F = t;

或者你可以在拉姆达(或任何可调用实体)的直接类型的模板调用点:

template <typename F>
int compute(int a, int b, F f)
{
    return a * f(b);  // example
}

用法: int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); } int a = 0; for (int i : { 1, 3, 5 }) { a += compute(10, i, t); }

如果可能的话,第二样式是优选的,因为转换到std::function是不平凡的,可能昂贵的操作中,当通过所得到的物体的实际的函数调用。 但是,如果你需要存储的异构可调用实体的统一收集,然后std::function可能是最简单,最方便的解决方案。



Answer 2:

Lambda表达式没有默认构造函数。 永远。 唯一的建设者,他们有时会给访问时(取决于它们捕获的)复制和/或移动的构造。

如果你创建没有公共默认构造函数对象,你会得到同样的错误。

在C ++ 17可以用解决这个constexpr拉姆达和operator+衰减到函数指针。 携带一个函数指针,并调用它A型很容易用auto模板参数。

在C ++ 11你得让有点哈克。

template<class F>
struct stateless_lambda_t {
  static std::aligned_storage_t< sizeof(F), alignof(F) >& data() {
    static std::aligned_storage_t< sizeof(F), alignof(F) > retval;
    return retval;
  };
  template<class Fin,
    std::enable_if_t< !std::is_same< std::decay_t<Fin>, stateless_lambda_t >{}, int> =0
  >
  stateless_lambda_t( Fin&& f ) {
    new ((void*)&data()) F( std::forward<Fin>(f) );
  }
  stateless_lambda_t(stateless_lambda_t const&)=default;
  template<class...Args>
  decltype(auto) operator()(Args&&...args)const {
    return (*static_cast<F*>( (void*)&data() ))(std::forward<Args>(args)...);
  }
  stateless_lambda_t() = default;
};
template<class F>
stateless_lambda_t<std::decay_t<F>> make_stateless( F&& fin ) {
  return {std::forward<F>(fin)};
}

现在,我们可以:

auto t = make_stateless([]{ func(); });

和你的代码工作。

一个static_assert或SFINAE该F实际上是一个空的类型可能是一个好主意。 要知道,对于品质。

使用C ++ 14个特征可以与手动更换decltype和喷涌typename::type的关键字。 这个答案最初被写为已关闭,因为这一个的副本一个C ++ 14的问题。

活生生的例子 。



Answer 3:

我们可以假设,拉姆达永远是空的,因此,我们可以做一个演员是另一个空类型,因为它们都具有相同的内存布局。 因此,我们建立了一个包装类,使一个缺省构造函数对象:

template<class F>
struct wrapper
{
    static_assert(std::is_empty<F>(), "Lambdas must be empty");
    template<class... Ts>
    auto operator()(Ts&&... xs) const -> decltype(reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...))
    {
        return reinterpret_cast<const F&>(*this)(std::forward<Ts>(xs)...);
    }
};

现在静态断言被添加到确保拉姆达总是空的。 这应该始终是这种情况(因为其所需的衰减函数指针),但该标准并没有明确保证。 因此,我们使用断言至少赶上疯狂实施lambda表达式。 然后,我们只投了包装类的拉姆达,因为他们两人都是空的。

最后,拉姆达可以构造是这样的:

template <class F>
void function_caller()
{
    wrapper<F> f;
    f();
}


Answer 4:

没有。

不过我相信,lambda表达式可以被复制,所以你functor_caller可以带参数初始化其属性。

尽管如此,而不是重新发明轮子,我会使用std::function来代替。



文章来源: Instantiating C++ lambda by its type