Lambda作为模板功能

时间:2016-05-14 20:38:45

标签: c++ templates c++11 lambda

我有一个非常奇怪的问题。为了简单起见,假设我想要一个函数,它使用与参数相同的声明来获取2个函数

template<typename Func>
void foo(Func a, Func b)
{
    std::cout << "good";
}

为了尝试一下,我从cstdio中获取了putchar,并创建了一个与putchar相匹配的相同函数。

int myPutcharFunc(int)
{
    return 0;
}

int main()
{
    auto myPutcharLambda = [](int) -> int
    {
        return 0;
    };

    foo(putchar, myPutcharFunc); // okay
    foo(putchar, myPutcharLambda);  //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>')
}

现在,lambda不想编译(关键是我想使用lambda捕获)。

所以我们添加模板专业化,因为程序员比机器更聪明,对吧? :)

template<typename Func>
void foo(Func a, Func b)
{
    std::cout << "good";
}

template<>
void foo(int(*)(int), int(*)(int))
{
    std::cout << "good";
}

没有运气,同样的错误 - 为什么? 但出于某种原因,当我注释掉模板专业化时:

//template<>
void foo(int(*)(int), int(*)(int))
{
    std::cout << "good";
}

代码编译。我显然不想为每一组函数的参数重载foo - 这就是模板的用途。每个步骤都使用msvc ++和g ++进行测试。我做错了什么?

3 个答案:

答案 0 :(得分:2)

每个lambda都是不同的类型,因此您需要有两个不同的模板参数来获取它们

template<typename FuncA, typename FuncB>
void foo(FuncA a, FuncB b)

类型在推断模板类型时不会衰减(请参阅修正注释)。因此lambda仍然是lambda并且不会衰减到函数指针。同样的原因,字符串文字被推断为char[N]而不是const char *

使用专业化的第二个示例,它不想使用您的专业化,因为lambda不是函数指针。您可以将Lambda转换为函数指针并使其工作:https://godbolt.org/g/ISgPci您可以在此处执行的技巧是+ my_lambda,因为+是为指针定义的,所以它会强制不捕获lambda成为函数指针。

答案 1 :(得分:2)

两种可能性。

1 :只需将+放在lambda前面:

foo(putchar, +myPutcharLambda);

这是有效的,因为一元+需要类似整数的值,例如指针。因此,lambda转换为函数指针。

最终,(非捕获)lambda与函数指针的类型不同,即使它愿意转换为函数指针。

编译器如何知道允许哪些转换生成两个相同类型的对象?

2 :还有另一种选择,即?:愿意进行一些转换converting one type to another in some circumstances。< / p>

template<typename Func1, typename Func2>
void foo2(Func1 a, Func2 b)
{
    using common_type = decltype(true?a:b); // or 'false', it doesn't matter
    foo<common_type>(a,b);
}

答案 2 :(得分:1)

lambda有自己的类型,它可以衰减到函数指针但不是在模板函数匹配的情况下,它会因为隐式转换而找到真正的函数。

在匹配模板的情况下,您需要消除歧义并使用您想要的类型显式实例化foo或将lambda转换为函数指针。

foo<decltype(putchar)>(putchar, myPutcharLambda);

foo(putchar, +myPutcharLambda);