Как передать шаблон функции в другую функцию?

241
26 ноября 2016, 18:57

Как правильно написать функцию test, чтобы ей в качестве аргумента (либо шаблонного параметра - не важно) можно было передать любую из функций f и g, с возможностью их вызова с разным типом аргумента (в примере int, char и long).

http://ideone.com/1aZ8Zu

#include <iostream>
using namespace std;
template <typename T> auto f(T x) -> decltype(x*x)
{
  return x * x;
}
template <typename T> T g(T x)
{
  return x * x;
}
template <template <class> class F> void test(F f)
{
    auto a = f(32);
    auto b = f(' ');
    auto c = f(2000000000L);
    cout << a << ' ' << b << ' ' << c << endl;
}
int main()
{
    test(f);
    test(g);
    return 0;
}

Этот код не компилируется с сообщениями:

prog.cpp:15:47: error: variable or field 'test' declared void
 template <template <class> class F> void test(F f)
                                               ^
prog.cpp:15:49: error: missing template arguments before 'f'
 template <template <class> class F> void test(F f)
                                                 ^
prog.cpp: In function 'int main()':
prog.cpp:26:8: error: 'test' was not declared in this scope
  test(f);
        ^
Answer 1

Могу порекомендовать в определенном смысле обходной, но в определенном - и прямой путь. В C++14 (а у вас четко указан этот тэг) лямбды тоже могут быть шаблонами.

auto f = [](auto x){ cout << x << endl; return x*x; };
template<typename F> void test(F f)
{
    auto a = f(32);
    auto b = f(' ');
    auto c = f(2000000000ull);
    cout << a << ' ' << b << ' ' << c << endl;
}
int main()
{
    test(f);
    return 0;
}

Вызывается f именно как шаблон (разная для разных аргументов).

Да, это не есть абсолютно строгий и точный ответ на заданный вопрос, но - шаблон в шаблоне вызван :)

Можно использовать лямбду и как уровень косвенности:

template <typename T> T g_(T x)
{
  return x * x * x;
}
auto g = [](auto x){ cout << typeid(x).name() << endl; return g_(x); };
template<typename F> void test(F f)
{
    auto a = f(32);
    auto b = f(' ');
    auto c = f(2000000000ull);
    cout << a << ' ' << b << ' ' << c << endl;
}
int main()
{
    test(g);
    return 0;
}

Пример на ideone: http://ideone.com/kfUWtx

Answer 2

Немного теории.

Для того, чтобы можно было вызывать f(42) и f(42L) одновременно - переменная f должна быть типа, у которого определен шаблонный оператор ():

struct F {
  template <typename T> void operator () (T value);
};
void test(F f); // работает

Если же переставить шаблон в другое место - то вы не сможете инстанцировать F:

template <typename T> struct F {
  void operator () (T value);
};
void test(F f); // ошибка компиляции: F - это шаблон типа, а не тип

Проблема с функциями - в том, что ссылка на шаблонную функцию больше похожа по своим свойствам на вторую форму записи, чем на первую.

Как уже написал @Harry - для неявного создания структуры первого типа можно использовать лябмды:

auto g = [](auto x){ return x*x; };
template<typename F> test(F f); // сюда можно передать переменную g
Answer 3

Это невозможно, в том виде, что Вы хотите. Вы имеете две шаблонных функции test и f, которые встречаются в одном выражении, и в которых тип аргумента должен быть выведен из переданных аргументов. Что может вывести из этой test(f) строчки компилятор? Ничего, т.к. нет никакой уточняющей информации, конкретизирующей, что за версию нужно инстанциировать.

READ ALSO
Как отредактировать .idb файл?

Как отредактировать .idb файл?

Есть коммерческое ПО, при каждом запуске оно подключается к файлу базы данных, файл именуется как uchetatiidb

257
Аналог битового поля на C#

Аналог битового поля на C#

Есть следующий код на C++:

357
Погрешность std::chrono::steady_clock

Погрешность std::chrono::steady_clock

Мне необходимо каждый час выполнять какие то действияСоздаю что то типа таймера с помощью std::chrono::steady_clock, в while проверяю или пришло время выполнять...

271
Готовые решения GUI - наподобие Steam/Origin

Готовые решения GUI - наподобие Steam/Origin

Подскажите где можно найти готовые решения(С исходным кодом) с модерн дизайном интерфейса, как это делают в Steam/Origin

324