Подтвердить что ты не робот

Упрощенный способ параметризации шаблона функции с помощью указателя общей функции

Рассмотрим следующий случай: 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, и я не могу его изменить в ближайшем будущем.

4b9b3361

Ответ 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);
}