我正在寻找涉及将c++模板函数作为参数传递的规则。

这是c++支持的,如下面的例子所示:

void add1(int &v) { v += 1 } 
void add2(int &v) { v += 2 }

template <void (*T)(int &)>
void doOperation()
{
  int temp = 0;
  T(temp);
  std::cout << "Result is " << temp << std::endl;
}

int main()
{
    doOperation<add1>();
    doOperation<add2>();
}

然而,学习这种技术是困难的。在谷歌上搜索“函数作为模板参数”并没有什么结果。令人惊讶的是,经典的c++模板完整指南也没有讨论它(至少从我的搜索中没有)。

我的问题是这是否是有效的c++(或者只是一些广泛支持的扩展)。

另外,在这种模板调用过程中,是否有一种方法允许具有相同签名的函子与显式函数互换使用?

下面的代码在上面的程序中不起作用,至少在Visual c++中不起作用,因为语法显然是错误的。如果能够将函数转换为函子(反之亦然)就很好了,类似于如果想定义自定义比较操作,可以将函数指针或函子传递给std::sort算法。

struct add3 {
    void operator() (int &v) {v += 3;}
};
...

doOperation<add3>();

指向一两个web链接的指针,或者c++模板书中的一个页面将是非常感谢的!


当前回答

编辑:将操作符作为引用传递是行不通的。为简单起见,将其理解为函数指针。你只是发送指针,而不是引用。 我想你是想写这样的东西。

struct Square
{
    double operator()(double number) { return number * number; }
};

template <class Function>
double integrate(Function f, double a, double b, unsigned int intervals)
{
    double delta = (b - a) / intervals, sum = 0.0;

    while(a < b)
    {
        sum += f(a) * delta;
        a += delta;
    }

    return sum;
}

. .

std::cout << "interval : " << i << tab << tab << "intgeration = "
 << integrate(Square(), 0.0, 1.0, 10) << std::endl;

其他回答

是的,是有效的。

至于让它与函子一起工作,通常的解决方案是这样的:

template <typename F>
void doOperation(F f)
{
  int temp = 0;
  f(temp);
  std::cout << "Result is " << temp << std::endl;
}

现在可以被称为:

doOperation(add2);
doOperation(add3());

现场观看

这样做的问题是,如果它使编译器内联对add2的调用变得棘手,因为编译器只知道函数指针类型void (*)(int &)被传递给doOperation。(但是add3是一个函子,可以很容易地内联。在这里,编译器知道一个add3类型的对象被传递给了函数,这意味着要调用的函数是add3::operator(),而不仅仅是某个未知的函数指针。)

编辑:将操作符作为引用传递是行不通的。为简单起见,将其理解为函数指针。你只是发送指针,而不是引用。 我想你是想写这样的东西。

struct Square
{
    double operator()(double number) { return number * number; }
};

template <class Function>
double integrate(Function f, double a, double b, unsigned int intervals)
{
    double delta = (b - a) / intervals, sum = 0.0;

    while(a < b)
    {
        sum += f(a) * delta;
        a += delta;
    }

    return sum;
}

. .

std::cout << "interval : " << i << tab << tab << "intgeration = "
 << integrate(Square(), 0.0, 1.0, 10) << std::endl;

你的函子例子不起作用的原因是你需要一个实例来调用operator()。

在模板中

template <void (*T)(int &)>
void doOperation()

形参T是非类型模板形参。这意味着模板函数的行为会随着形参值的变化而变化(形参值必须在编译时固定,即函数指针常量)。

如果你想同时使用函数对象和函数参数,你需要一个类型化模板。但是,当您这样做时,还需要在运行时为函数提供一个对象实例(函数对象实例或函数指针)。

template <class T>
void doOperation(T t)
{
  int temp=0;
  t(temp);
  std::cout << "Result is " << temp << std::endl;
}

There are some minor performance considerations. This new version may be less efficient with function pointer arguments as the particular function pointer is only derefenced and called at run time whereas your function pointer template can be optimized (possibly the function call inlined) based on the particular function pointer used. Function objects can often be very efficiently expanded with the typed template, though as the particular operator() is completely determined by the type of the function object.

函数指针可以作为模板参数传递,这是标准c++的一部分 . 然而,在模板中,它们被声明并作为函数而不是作为函数指针使用。在模板实例化时,传递函数的地址而不仅仅是名称。

例如:

int i;


void add1(int& i) { i += 1; }

template<void op(int&)>
void do_op_fn_ptr_tpl(int& i) { op(i); }

i = 0;
do_op_fn_ptr_tpl<&add1>(i);

如果你想传递一个函子类型作为模板参数:

struct add2_t {
  void operator()(int& i) { i += 2; }
};

template<typename op>
void do_op_fntr_tpl(int& i) {
  op o;
  o(i);
}

i = 0;
do_op_fntr_tpl<add2_t>(i);

有几个答案将一个函数实例作为参数传递:

template<typename op>
void do_op_fntr_arg(int& i, op o) { o(i); }

i = 0;
add2_t add2;

// This has the advantage of looking identical whether 
// you pass a functor or a free function:
do_op_fntr_arg(i, add1);
do_op_fntr_arg(i, add2);

使用模板参数最接近这种统一外观的方法是定义do_op两次——一次使用非类型形参,一次使用类型形参。

// non-type (function pointer) template parameter
template<void op(int&)>
void do_op(int& i) { op(i); }

// type (functor class) template parameter
template<typename op>
void do_op(int& i) {
  op o; 
  o(i); 
}

i = 0;
do_op<&add1>(i); // still need address-of operator in the function pointer case.
do_op<add2_t>(i);

老实说,我真的希望这个不能编译,但它在gcc-4.8和Visual Studio 2013中为我工作。