Упрощенный способ параметризации шаблона функции с помощью указателя общей функции
Рассмотрим следующий случай: I
int bar1();
double bar2();
Я хочу:
foo<bar1>(); // calls bar1, then uses its result.
foo<bar2>(); // calls bar2, then uses its result.
Наивный способ написать шаблон foo - использовать дополнительный параметр:
template <typename T, T (*f)()> void foo () {
// call f, do something with result
}
Это работает, но мне нужно сделать уродливый синтаксис:
foo<decltype(bar1()), bar1>(); // calls bar1, then uses its result
Я хочу написать что-то красивое, как показано выше, только foo<bar1>
.
P.S. Не рекомендуется принимать аргумент во время выполнения. Мне нужна компиляция времени параметризации только с указателем функции.
P.S. Извините забудьте отметить: Я ищу С++ 14 решение. С++ 17 оценил, и я ответил на ответ с помощью решения С++ 17, но проект теперь строит с С++ 14, и я не могу его изменить в ближайшем будущем.
Ответы
Ответ 1
Чтобы получить
foo<bar1>();
Вам нужно template<auto>
от С++ 17. Это будет выглядеть как
int bar1() { return 1; }
double bar2() { return 2.0; }
template<auto function> void foo() { std::cout << function() << "\n"; }
int main()
{
foo<bar1>();
foo<bar2>();
}
Какие выходы
1
2
Живой пример
Перед С++ 17 вы должны указать тип, так как нет автоматического дедукции типа параметров шаблона без типа.
Ответ 2
Итак, я постараюсь дать наилучший ответ, о котором я знаю в 14. В принципе хороший подход (IMHO) к этой проблеме заключается в том, чтобы "поднять" указатель на функцию лямбда. Это позволяет вам писать foo
гораздо более идиоматичным способом принятия вызываемого:
template <class F>
void foo(F f);
Вы по-прежнему получаете оптимальную производительность, потому что тип лямбда уникален, и поэтому он становится встроенным. Вы можете более легко использовать foo с другими вещами. Итак, теперь мы должны превратить наш указатель функции в лямбду, которая жестко запрограммирована для ее вызова. Самое лучшее, что мы можем на этом фронте, взято из этого вопроса: Функция для лямбда.
template <class T>
struct makeLambdaHelper;
template <class R, class ... Args>
struct makeLambdaHelper<R(*)(Args...)>
{
template <void(*F)(Args...)>
static auto make() {
return [] (Args ... args) {
return F(std::forward<Args>(args)...);
};
}
};
Мы используем его следующим образом:
auto lam = makeLambdaHelper<decltype(&f)>::make<f>();
Чтобы не упоминать его дважды, мы можем использовать макрос:
#define FUNC_TO_LAMBDA(f) makeLambdaHelper<decltype(&f)>::make<f>()
Затем вы можете:
foo(FUNC_TO_LAMBDA(bar1));
Пример в реальном времени: http://coliru.stacked-crooked.com/a/823c6b6432522b8b
Ответ 3
Я ищу решение на С++ 14. С++ 17 оценил, и я ответил на ответ с помощью решения С++ 17, но проект теперь строит с С++ 14, и я не могу его изменить в ближайшем будущем.
К сожалению, вы просите работу, начиная с С++ 17.
Если вы хотите использовать точно синтаксис
foo<bar1>();
Я не думаю, что это возможно в С++ 14.
Но, если вы принимаете немного другой синтаксис... Я знаю, что макросы - это дистиллированное зло, но... если вы принимаете вызов foo()
как
FOO(bar1)();
вы можете определить макрос
#define FOO(f) foo<decltype(f()), f>
Полный рабочий пример
#include <iostream>
#define FOO(f) foo<decltype(f()), f>
int bar1 ()
{ std::cout << "bar1()" << std::endl; return 0; }
double bar2 ()
{ std::cout << "bar2()" << std::endl; return 1.0; }
template <typename T, T (*f)()>
void foo ()
{ f(); }
int main()
{
FOO(bar1)(); // print bar1()
FOO(bar2)(); // print bar2()
}
Ответ 4
ОК, поэтому вы специально задали для bar1
и bar2
функции, но если бы вы захотели расслабиться это ограничение и вместо этого позволить им быть классами, имеющими статическую функцию-член, которая реализует желаемое поведение, которое вы могли бы сделать это следующим образом, что даже не требует С++ 11 -
struct bar1 {
static int f() { return 42; }
};
struct bar2 {
static double f() { return 3.14159; }
};
template<typename bar>
void foo()
{
double x = bar::f();
std::cout << x << std::endl;
}
int main(int argc, char* const argv[])
{
foo<bar1>();
foo<bar2>();
}
Ответ 5
Как насчет этого?
template<typename F>
auto foo(F* f)
{
return f();
}
int bar() { return 1; }
int main()
{
return foo(bar);
}