将值 caputring lambda 传递给需要函数指针的函数

Passing a value caputring lambda to a function which expects a function pointer

我有一个 class foo,我想向其传递类型 T 的对象以及带有签名 void (T) 的“类删除器”函数。目前我将这个函数存储为 typedef void (*bar_t)(T); 类型的对象 m_bar inside foo.

只要我要使用的函数与上述签名完全匹配,这就没有问题。但是,如果所需的函数有一个附加参数,并且我需要将一个特定的非常量值传递给它,我会尝试传递一个 lambda 函数,该函数按值捕获该值;但现在我在下面的示例代码中收到编译器错误 cannot convert from 'initializer list' to 'foo<int>': `

template<typename T>
struct foo
{
    typedef void (*bar_t)(T);

    foo(T t, bar_t bar)
        : t(t),
          bar(bar)
    {}

    ~foo() { bar(t); }

    T t;
    bar_t bar;
};

void bar1(int x) {}
void bar2(int* x) {}
void bar3(int, int* x) {}

template<typename T, class Bar>
foo<T> make_foo(T t, Bar bar) { return { t, bar }; }

int main()
{
    int x = 47, y = 0;
    
    foo f1(x, bar1);
    foo f2(x, [](int x) { bar2(&x); });
    auto f3a = make_foo(x, [y](int x) { bar3(0, &x); }); // ok
    auto f3b = make_foo(x, [y](int x) { bar3(y, &x); }); // compiler error

}

我们可以在不在 foo 中存储 std::function 的情况下解决这个问题吗?

你基本上有两个选择:

  • 添加额外的模板参数:

    template<typename T, typename DeleterLike>
    struct foo
    {
        using bar_t = DeleterLike;
    
        foo(T t, bar_t bar)
            : t(t),
              bar(bar)
        {}
    
        ~foo() { bar(t); }
    
        T t;
        bar_t bar;
    };
    
  • 对类型使用一些类型擦除(如std::function

    template<typename T>
    struct foo
    {
        using bar_t = std::function<void(const T&)>;
    
        foo(T t, bar_t bar)
            : t(t),
              bar(bar)
        {}
    
        ~foo() { bar(t); }
    
        T t;
        bar_t bar;
    };
    

在编译时知道 y 值的非常有限的情况下,这有效:

template <typename T>
struct foo
{
  typedef void (*bar_t)(T);

  foo(T t, bar_t bar) : t(t), bar(bar) {}

  ~foo() { bar(t); }

  T t;
  bar_t bar;
};

template <typename T, typename Y, Y y>
auto create_lambda()
{
  return [](T x) { std::cout << x + y << "\n"; };
}

int main()
{
  foo<double> f1(1.5, create_lambda<double, int, 1>());
  foo<double> f2(1.5, create_lambda<double, int, 2>());
}

并打印

3.5
2.5