将具有默认参数的lambda函数复制到变量

时间:2016-06-09 00:36:55

标签: c++ c++11 lambda

请考虑以下代码:

#include <iostream>
#include <functional>
using namespace std;

int main() {
    auto f = [](int a = 3) {cout << a << endl; };
    f(2); // OK
    f();  // OK

    auto g = f;
    g(2); // OK
    g();  // OK

    function<void(int)> h = f;
    h(2); // OK
    h();  // Error! How to make this work?

    return 0;
}

如何声明h的行为与fg相同?

2 个答案:

答案 0 :(得分:12)

std::function有一个固定的签名。这是一个设计选择,而不是硬性要求。编写支持多个签名的伪std::function并不难:

template<class...Sigs>
struct functions;

template<>
struct functions<> {
  functions()=default;
  functions(functions const&)=default;
  functions(functions&&)=default;
  functions& operator=(functions const&)=default;
  functions& operator=(functions&&)=default;
private:
  struct never_t {private:never_t(){};};
public:
  void operator()(never_t)const =delete;

  template<class F,
    std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
  >
  functions(F&&) {}
};

template<class S0, class...Sigs>
struct functions<S0, Sigs...>:
  std::function<S0>,
  functions<Sigs...>
{
  functions()=default;
  functions(functions const&)=default;
  functions(functions&&)=default;
  functions& operator=(functions const&)=default;
  functions& operator=(functions&&)=default;
  using std::function<S0>::operator();
  using functions<Sigs...>::operator();
  template<class F,
    std::enable_if_t<!std::is_same<std::decay_t<F>, functions>{}, int>* =nullptr
  >
  functions(F&& f):
    std::function<S0>(f),
    functions<Sigs...>(std::forward<F>(f))
  {}
};

使用:

auto f = [](int a = 3) {std::cout << a << std::endl; };

functions<void(int), void()> fs = f;
fs();
fs(3);

Live example

这将为每个重载创建一个单独的lambda副本。通过精心铸造,甚至可以为不同的过载设置不同的lambda。

你可以写一个不这样做的,但它基本上需要用更高级的内部状态重新实现std::function

上述更高级版本将避免使用线性继承,因为这会导致O(n ^ 2)代码和O(n)递归模板深度的签名数量。平衡二叉树继承将生成的O(n lg n)代码和O(lg n)深度降低。

工业强度版本将存储传入的lambda一次,使用小对象优化,使用手动伪vtable使用二进制继承策略来调度函数调用,并在所述伪vtable中存储调度到函数指针。它将在每个类(不是每个实例)的基础上使用O(#ignatures)* sizeof(函数指针)空间,并使用与std::function一样多的每实例开销。

但这对SO帖子来说有点多,不是吗?

start of it

答案 1 :(得分:2)

我喜欢@Yakk提出的很好的解决方案。

也就是说,你可以通过避免std::function并使用lambda,变量模板和std::forward的代理函数来做类似的事情,如下所示:

#include<functional>
#include<utility>
#include<iostream>

template<typename... Args>
void f(Args&&... args) {
    auto g = [](int a = 3) { std::cout << a << std::endl; };
    g(std::forward<Args>(args)...);
};

int main() {
    f(2);
    f();
}

将上述想法放在仿函数中,你将拥有一个类似于函数的对象,几乎完全相同。

存在参数未经严格验证的问题 无论如何,如果误用,它将在编译时失败。