使用默认参数值作为模板参数传递函数

时间:2016-05-19 05:26:52

标签: c++ templates

我对以下行为感到有点困惑。我传递一个带有两个参数的函数,一个具有默认值,作为模板参数,并使用一个参数调用该函数。为什么编译失败?什么是解决方案/解决方法?

#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);
          ^

3 个答案:

答案 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可以更改为floatint。编译器不会假设 float。同样,只是为了成功编译,编译器不会尝试找到sum的最佳匹配。