Возможно ли иметь функцию (-name) в качестве параметра шаблона в С++?
Мне не нужен служебный указатель указателя функции, я просто хочу, чтобы тот же код для двух разных функций с одной и той же сигнатурой:
void f(int x);
void g(int x);
...
template<typename F>
void do_work()
{
int v = calculate();
F(v);
}
...
do_work<f>();
do_work<g>();
Возможно ли это?
Чтобы устранить возможную путаницу: с "параметром шаблона" я имею в виду параметр/аргумент шаблона и не параметр функции, тип которого настроен templated.
Ответы
Ответ 1
Один подход, который с большой вероятностью может вызвать прямой вызов функции, поскольку он не дает опции компилятора no, заключается в использовании статической функции-члена:
struct F { static void func(int x) { /*whatever*/ } };
struct G { static void func(int x) { /*whatever*/ } };
template<class T>
void do_work() {
T::func(calculate());
}
Нет указателей на функции, нет временных рядов и нет ненужных this
. Конечно, я ничего не гарантирую, но сгенерированный код должен быть разумным даже при отключенной оптимизации.
Ответ 2
Ваша идея в порядке, но вы не передаете тип, а значение (в частности, указатель на функцию). В качестве альтернативы передайте функции политики шаблонов - это хорошая идея прочитать Modern С++ Design от Andrei Alexandrescu.
#include <iostream>
int f(int x) { return 2 * x; }
int g(int x) { return -3 * x; }
typedef int (*F)(int);
template<F f>
int do_work()
{
return f(7);
}
int main()
{
std::cout << do_work<f>() << '\n'
<< do_work<g>() << '\n';
}
ИЛИ
int calculate() { return 4; }
struct F { int do_something_with(int x) { return 2 * x; } };
struct G { int do_something_with(int x) { return -3 * x; } };
// or, make these functions static and use Operator::do_something_with() below...
template<typename Operation>
int do_work()
{
int v = calculate(7);
return Operation().do_something_with(v);
}
int main()
{
std::cout << do_work<F>() << '\n'
<< do_work<G>() << '\n';
}
Ответ 3
У вас могут быть указатели на функции в качестве параметров шаблона, но функциональные объекты больше "С++ ish". Однако вы можете написать свой шаблон функции таким образом, чтобы он принимал оба варианта:
#include <iostream>
void f(int x)
{
std::cout << "inside function f\n";
}
struct g
{
void operator()(int x)
{
std::cout << "inside function object g\n";
}
};
template <typename Functor>
void do_work(Functor fun)
{
fun(42);
}
int main()
{
// template argument is automatically deduced
do_work(&f);
// but we could also specify it explicitly
do_work<void(*)(int)>(&f);
// template argument is automatically deduced
do_work(g());
// but we could also specify it explicitly
do_work<g>(g());
}
Здесь имя Functor
намекает на любой тип, вызываемый с помощью синтаксиса f(x)
. Функции поддерживают этот синтаксис естественно, а в случае объектов функций f(x)
- синтаксический сахар для f.operator()(x)
.
Ответ 4
Нет, вам нужно обернуть функции в класс-оболочку operator()
. Вот пример:
class Functor_f
{
public:
void operator()(int x)
{
}
};
class Functor_g
{
public:
void operator()(int x)
{
}
};
template<typename F>
void do_work()
{
F f;
int v = calculate();
f(v);
}
int main()
{
do_work<Functor_f>();
do_work<Functor_g>();
}
Вы можете использовать std::ptr_fun
, чтобы сделать это автоматически для вас. Например:
void f(int x)
{
}
void g(int x)
{
}
template<typename F>
void do_work(F f)
{
int v = calculate();
f(v);
}
int main()
{
do_work(std::ptr_fun(f));
do_work(std::ptr_fun(g));
}