我有一个非常奇怪的问题。为了简单起见,假设我想要一个函数,它使用与参数相同的声明来获取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 ++进行测试。我做错了什么?
答案 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);