我对以下行为感到有点困惑。我传递一个带有两个参数的函数,一个具有默认值,作为模板参数,并使用一个参数调用该函数。为什么编译失败?什么是解决方案/解决方法?
#include <iostream>
using namespace std;
template<typename Function>
void eval(Function function) {
function(10);
}
void sum(int i, int j = 0) {
cout << "Sum is " << i + j;
}
int main() {
sum(10); // OK, of course :)
eval(sum); // Error!
}
请注意,这个问题与使用默认参数调用模板化函数无关。
错误消息:
prog.cpp: In instantiation of 'void eval(Function) [with Function = void (*)(int, int)]':
prog.cpp:15:10: required from here
prog.cpp:6:10: error: too few arguments to function
function(10);
^
答案 0 :(得分:5)
那是因为可选参数是函数声明的一部分。当您使用函数指针调用时,基本上所有编译器都知道函数指针的类型。因此,此行function(10)
大致转换为:
void (*)(int, int) sm = function; // the equivalent happens at the type deduction step
sm(10); // whoops, where is the second int
编译器将需要第二个参数,因为它无法知道sm
是否指向具有默认参数的sum
,或者某个其他void foo(int a, int b)
是否具有默认参数默认参数。
答案 1 :(得分:3)
这就是函数指针很弱的原因。你有两个很好的答案为什么这不起作用 - 函数模板不知道你的函数有一个默认参数。
解决方案是不传递函数指针 - 传入函数对象。在这种情况下,您可以手写lambda:
eval([](int i){ return sum(i); });
这似乎是一种非常烦人的冗长方式,基本上只是以实际工作的方式编写sum
。但是这种事情会定期出现(如果sum
是一个重载名称怎么办?)所以只需要一个lambdafying宏就很方便(需要C ++ 14):
#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(auto) { \
return func(std::forward<decltype(args)>(args)...); }
所以你可以写:
eval(AS_LAMBDA(sum));
这将适用于默认参数,重载名称,甚至可以绑定成员函数:
struct X {
int i;
int add(int j) { return i + j; }
};
X x{42};
eval(AS_LAMBDA(x.add));
答案 2 :(得分:2)
致电sum
时:
sum(10);
编译器将其转换为:
sum(10, 0);
没有错误,因为参数匹配。对于编译器,没有sum
采用一个参数。您可以说预编译器可以将0
传递给sum
的第二个参数。使用模板,这个神奇的预编译器不存在,并且实际的编译器将sum
与两个严格的两个参数匹配,它们找不到,因此错误。
就像
一样void foo(int);
void foo(float);
将无法使用foo(10.2);
进行编译,其中参数double
可以更改为float
或int
。编译器不会假设 float
。同样,只是为了成功编译,编译器不会尝试找到sum
的最佳匹配。