将具有不同签名的lambda传递给泛型函数

时间:2018-03-29 03:17:37

标签: c++ c++11

考虑以下玩具示例:

#include <iostream>

template <typename T>
void foo(T func, int *i) {
    if (i) {
        func(*i);
    } else {
        func();
    }
}

int main() {
    auto n = new int{2};
    foo([](int x) { std::cout << x << std::endl; }, n);
    foo([]() { std::cout << "Foo" << std::endl; }, nullptr);
    return 0;
}

我想创建一个函数,它接收具有不同签名的lambdas函数,并在其范围内调用它们。如果我不在foo内调用lambda函数,它编译得很好,但如果我调用它,代码就不会编译。

有没有办法接收具有不同签名的lambdas并在函数中调用它?

2 个答案:

答案 0 :(得分:4)

在功能模板中,必须将T确定为一种特定类型,并在整个实例化过程中保持该类型。

然而,您可以通过使foo成为可变参数模板,并将参数包传递给传入的lambda来完成您想要的任务:

#include <iostream>

template <typename T, typename ...Args>
void foo(T func, Args... args) {
    func(std::forward<Args>(args)...);
}

int main() {
    auto n = new int{ 2 };
    foo([](int x) { std::cout << x << std::endl; }, n[0]);
    foo([]() { std::cout << "Foo" << std::endl; });
    foo([](int a, int b) { std::cout << a + b << "\n"; }, 3, 2);
}

这种方式foo只需要包含一个func的调用,它在每个实例化中都有一致的类型 - 但是可以从一个实例化到下一个实例化。

在这种情况下,当您传递的函数不接受任何参数时,您不会为参数传递特殊值。如果没有参数,你就不要传递参数。

答案 1 :(得分:1)

在第一种使用以下方法实例化模板函数的情况下:

foo([](int x) { std::cout << x << std::endl; }, n);

编译器创建函数,其中(T func)是一个可调用的函数,因此你不能这样做:

void foo(T func, int *i) {
    if (i) {
        func(*i);
    }
    //else 
    //{
    //  func(); // Can't call T func with no argument.
    //} // Commenting this out will compile.
}

使用以下方法实例化模板函数时

foo([]() { std::cout << "Foo" << std::endl; }, nullptr);

编译器创建函数:

void foo(T func, int *i); // Where T func is a callable taking no arguments

因此,与前面的示例相比,您必须在没有给出参数的情况下调用T func。因此你做不到:

template <typename T>
void foo(T func, int *i) 
{
    //if (i) {
    //  func(*i); // Can't call T func with one argument, because 
                  // T func function template has been instantiated 
                  // as T func taking no arguments.
    //}
    //else 
    {
        func(); // This obviously is fine.
    }
}