函数作为模板parameter passing
我正在寻找涉及传递C ++模板函数作为参数的规则。
这由C ++支持,如下例所示:
#include <iostream> 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>(); }
但是,了解这种技术是困难的。 谷歌search“function作为模板参数”不会导致太多。 而经典的C ++模板完全指南令人惊讶地也不讨论它(至less不是我的search)。
我有的问题是这是否是有效的C ++(或只是一些广泛支持的扩展)。
另外,有没有办法让一个具有相同签名的仿函数在这种模板调用过程中与显式函数交替使用?
在上面的程序中至less在Visual C ++中不起作用,因为语法显然是错误的。 如果你想定义一个自定义的比较操作,那么能够为一个函子转换一个函数是相当不错的,反之亦然,类似于你可以传递一个函数指针或函子到std :: sortalgorithm的方式。
struct add3 { void operator() (int &v) {v+=3;} }; ... doOperation<add3>();
指向一个或两个网页链接,或在C ++模板书中的页面将不胜感激!
是的,这是有效的。
至于使它与仿函数一起工作,通常的解决scheme是这样的:
template <typename F> void doOperation(F f) { int temp=0; f(temp); std::cout << "Result is " << temp << std::endl; }
现在可以称之为:
doOperation(add2); doOperation(add3());
这样做的问题是,如果编译器将内联调用add2
变得棘手,因为所有的编译器都知道函数指针typesvoid (*)(int &)
被传递给doOperation
。 (但是add3
是一个函子,可以很容易地被内联,在这里,编译器知道一个types为add3
的对象被传递给函数,这意味着要调用的函数是add3::operator()
,而不仅仅是一些未知函数指针)。
模板参数可以通过types(typename T)或值(int X)进行参数化。
模板化一段代码的“传统”C ++方式是使用一个仿函数 – 也就是说,代码位于一个对象中,因此该对象赋予代码唯一的types。
当使用传统函数时,这种技术不能很好地工作,因为types的改变并不表示一个特定的函数,而只是指定了许多可能函数的签名。 所以:
template<typename OP> int do_op(int a, int b, OP op) { return op(a,b); } int add(int a, int b) { return a + b; } ... int c = do_op(4,5,add);
不等同于函子的情况。 在这个例子中,do_op被实例化为其签名为int X(int,int)的所有函数指针。 编译器必须非常积极地完全内联这种情况。 (我不排除,因为编译器优化已经相当先进。)
有一种方法可以告诉我们这个代码并不完全符合我们的要求:
int (* func_ptr)(int, int) = add; int c = do_op(4,5,func_ptr);
仍然是合法的,显然这不是内联。 为了获得完整的内联,我们需要通过值进行模板化,所以模板中的函数是完全可用的。
typedef int(*binary_int_op)(int, int); // signature for all valid template params template<binary_int_op op> int do_op(int a, int b) { return op(a,b); } int add(int a, int b) { return a + b; } ... int c = do_op<add>(4,5);
在这种情况下,do_op的每个实例化的版本都被一个已经可用的特定函数实例化。 因此,我们期望do_op的代码看起来很像“return a + b”。 (Lisp程序员,停止你的傻笑!)
我们也可以证实,这更接近我们想要的,因为这个:
int (* func_ptr)(int,int) = add; int c = do_op<func_ptr>(4,5);
将无法编译。 GCC说:“错误:'func_ptr'不能出现在常量expression式中,换句话说,我不能完全展开do_op,因为在编译时我没有足够的信息来知道我们的操作是什么。
所以如果第二个例子真的完全embedded我们的op,而第一个例子没有,模板有什么好处呢? 它在做什么? 答案是:types强制。 第一个例子中的这个重复将起作用:
template<typename OP> int do_op(int a, int b, OP op) { return op(a,b); } float fadd(float a, float b) { return a+b; } ... int c = do_op(4,5,fadd);
这个例子将工作! (我不是说它是好的C ++,但是…)发生了什么事情是do_op被模板化在各种函数的签名周围,每个单独的实例化将写入不同types的强制代码。 所以使用fadd的do_op的实例化代码如下所示:
convert a and b from int to float. call the function ptr op with float a and float b. convert the result back to int and return it.
相比之下,我们的值事例要求函数参数完全匹配。
在你的模板中
template <void (*T)(int &)> void doOperation()
参数T
是一个非types的模板参数。 这意味着模板函数的行为会随着参数值的变化而变化(在编译时必须固定哪个函数指针常量)。
如果你想要兼容函数对象和函数参数,你需要一个input模板。 当你这样做的时候,你还需要在运行时为该函数提供一个对象实例(函数对象实例或函数指针)。
template <class T> void doOperation(T t) { int temp=0; t(temp); std::cout << "Result is " << temp << std::endl; }
有一些小的性能考虑。 这个新版本的函数指针参数可能效率较低,因为特定的函数指针只在运行时被取消引用和调用,而您的函数指针模板可以基于所使用的特定函数指针进行优化(可能是函数调用内联)。 函数对象通常可以用types化的模板非常有效地扩展,但是特定的operator()
完全由函数对象的types决定。
函数指针可以作为模板parameter passing, 这是标准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);
如果你想传递一个仿函数types作为模板参数:
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
两次,一次是非types参数,一次是types参数。
// 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。
你的函子的例子不起作用的原因是你需要一个实例来调用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;