21

我想要一种从函数中制作函子的方法。现在我尝试通过 lambda 函数包装函数调用并稍后实例化它。但是编译器说 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>()
4

4 回答 4

8

代码没有意义。想象一下,你有一个像这样的捕获 lambda:

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

默认构造类型的对象可能意味着什么decltype(t)

正如@Matthieu 建议的那样,您可以将 lambda 包装到一个function对象中:

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

或者您可以直接在 lambda 类型(或任何可调用实体)上模板您的调用站点:

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

如果可能的话,第二种风格更可取,因为转换std::function为非平凡的、可能昂贵的操作,通过结果对象的实际函数调用也是如此。但是,如果您需要存储异构可调用实体的统一集合,那么std::function可能是最简单和最方便的解决方案。

于 2012-06-17T19:35:57.607 回答
8

Lambda 没有默认构造函数。曾经。他们有时可以访问的唯一构造函数是(取决于它们捕获的内容)复制和/或移动构造函数。

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

constexpr在 C++17 中,您可以使用lambda 和operator+衰减函数指针来解决这个问题。带有函数指针并调用它的类型很容易使用auto模板参数。

在 C++11 中,你得有点 hacky。

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(); });

并且您的代码有效。

Astatic_assert或 SFINAEF实际上是一个空类型可能是个好主意。你知道,为了质量。

使用 C++14 特性可以用手动decltype和 spewingtypename::type关键字来代替。这个答案最初是为一个 C++14 问题编写的,该问题作为这个问题的副本关闭。

活生生的例子

于 2016-09-29T13:08:46.793 回答
7

我们可以假设 lambda 将始终为空,因此,我们可以从另一个空类型进行强制转换,因为它们都具有相同的内存布局。因此,我们构建了一个包装类,该类制作了一个默认的可构造函数对象:

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 始终为空。这应该总是如此(因为它需要衰减到函数指针),但标准并没有明确保证它。所以我们使用断言至少可以捕捉到 lambdas 的疯狂实现。然后我们只是将包装类转换为 lambda,因为它们都是空的。

最后可以像这样构造 lambda:

template <class F>
void function_caller()
{
    wrapper<F> f;
    f();
}
于 2014-09-30T23:57:15.470 回答
2

不。

但是我相信可以复制 lambda,因此您functor_caller可以使用参数来初始化其属性。

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

于 2012-06-17T17:01:32.673 回答