考虑这个假设的代码片段:
template<?? function>
void loop() {
for(int i =0; i < 10000; ++i ) {
function(i)
}
}
//...
void print(int i) {
std::cout << i << '\n';
}
//...
loop<print>();
是否有可能在C ++中做类似的事情?到目前为止,我知道函数指针和泛型仿函数可以通过模板参数传递(比如在std :: sort中),但有没有办法使它在运行时没有传递实际对象,并且调用“print”是完全直接(即没有间接)?即通过模板中的“值”传递实际函数,就像使用template <int i>
或其他一些整数类型在模板中传递整数一样。
答案 0 :(得分:3)
当然,这是可能的。模板非类型参数可以是函数指针类型。在最简单的情况下,专门针对您的简单示例定制,它可能如下所示
template <void (*function)(int)>
void loop() {
for(int i = 0; i < 10000; ++i ) {
function(i);
}
}
请注意,C ++ 03声明此类模板的有效模板参数必须是指向具有外部链接的函数的指针。 C ++ 11删除了外部链接要求。
答案 1 :(得分:2)
正如其他答案中所指出的,存在功能模板参数之类的东西。但是,有时希望模板参数是一种类型。这对于其他类型的模板元编程特别有用,例如,制作一系列功能。
以下代码允许您将函数包装在类型中。用于包装函数的WRAP_FUNC
宏有不同的变体。无后缀的模板用于模板之外,_T
变体用于模板中,_TI
变体用于在模板中继承(见下文)。
请注意,为了使用引用,使用std::forward
。此外,它必须使用另一个参数包CallArgs
编写,用于调用call()
的参数类型,而不是Args
,它们是参数类型给定的功能。
#include <utility>
#include <stdio.h>
// This goes into a header.
template <typename R, typename... Args>
struct Helper {
template <R (*Func) (Args...)>
struct Wrapper {
template <typename... CallArgs>
static R call (CallArgs && ... args)
{
return Func(std::forward<CallArgs>(args)...);
}
};
};
template <typename R, typename... Args>
struct Helper<R, Args...> MakeHelper (R (*func) (Args...));
#define WRAP_FUNC(func) decltype(MakeHelper(func))::Wrapper<func>
#define WRAP_FUNC_T(func) typename decltype(MakeHelper(func))::template Wrapper<func>
#define WRAP_FUNC_TI(func) decltype(MakeHelper(func))::template Wrapper<func>
// Let's try it out.
static double test_func (int x, double y)
{
return x + y;
}
using TestFunc = WRAP_FUNC(test_func);
template <typename Func>
static void CallFunc ()
{
double r = Func::call(4, 6.0);
printf("%f\n", r);
}
int main ()
{
CallFunc<TestFunc>();
}
如果函数只能在传递给需要它的类之后定义(因为它本身调用调用它的类,并且我们不想将它的定义和声明分开),可以使用继承解决循环依赖。这里,如果宏在模板中,则需要使用宏的_TI
形式。
template <.....>
class Qux {
struct CallbackFunc;
using MyFoo = Foo<CallbackFunc>;
static void callback_func ()
{
// Foo calls us but we call Foo!
MyFoo::bar();
}
struct CallbackFunc : public WRAP_FUNC_TI(callback_func) {};
};
答案 2 :(得分:2)
一个更简单的功能模板,没有任何花里胡哨的东西:)
#include <iostream>
template<typename Function>
void loop(Function function) {
for(int i =0; i < 10000; ++i ) {
function(i);
}
}
void print(int i) {
std::cout << i << '\n';
}
int main()
{
loop(print);
return 0;
}
答案 3 :(得分:1)
由于您可以将指针作为非类型参数传递给模板,因此您也可以将函数指针传递给它们。您也可以考虑传递函数对象。
#include <iostream>
template <void (*f)()>
void loop() {
f();
}
template <typename F>
void loop() {
F()();
}
void F() {
std::cout << "F" << std::endl;
}
struct G {
void operator()() const {
std::cout << "G" << std::endl;
}
};
int main() {
loop<F>();
loop<G>();
}
打印
F
G
答案 4 :(得分:0)
无法使用函数而不是函数指针作为模板参数。很少有实体可以用作模板参数。非类型模板参数列在14.1 [temp.param]第4段中:
非类型模板参数应具有以下(可选的cv限定)类型之一:
- 整数或枚举类型,
- 指向对象或指向函数的指针,
- 对对象的左值引用或对函数的左值引用,
- 指向成员的指针,
- 的std :: nullptr_t。