Вывод шаблона С++ из лямбда

У меня есть функция, которая принимает две аргумента std::function. Параметр второй функции имеет тот же тип, что и результат первого.

Я написал шаблон функции следующим образом:

template<typename ResultType>
void examplFunction(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
  auto x = func();
  func2(x);
}

Я могу позвонить:

void f() {
  examplFunction<int>([]() { return 1; },   //
                      [](int v) { std::cout << "result is " << v << std::endl; });
}

Есть ли способ избавиться от <int> на examplFunction<int> и позволить компилятору вывести тип ResultType?

Ответ 1

Вам действительно нужна std::function? std::function полезна, когда вам нужно стирать стили. С шаблонами вы можете вообще пропустить его:

template<class F1, class F2>
void examplFunction(F1 func, F2 func2, decltype(func2(func()))* sfinae = nullptr) {
  auto x = func();
  func2(x);
}

Параметр sfinae гарантирует, что функция может быть вызвана только с такими функциями, что func2 можно вызвать с результатом func.

Ответ 2

Да, есть.

template<typename ResultType>
void examplFunction_impl(std::function<ResultType()> func, std::function<void(ResultType)> func2) {
    auto x = func();
    func2(x);
}

template<class F1, class F2>
void examplFunction(F1&& f1, F2&& f2)
{
    using ResultType = decltype(f1());
    examplFunction_impl<ResultType>(std::forward<F1>(f1), std::forward<F2>(f2));
}

демонстрация

В этом случае вам нужно, чтобы f1 был invocable без аргументов, поэтому вы можете определить тип возврата в вспомогательной функции. Затем вы вызываете реальную функцию, явно указывая тип возврата.

Вы можете добавить SFINAE, чтобы убедиться, что эта функция участвует только в разрешении перегрузки, когда f1 действительно может быть вызван таким образом (и если f2 также можно вызвать с возвращаемым значением f1).

Хотя я должен согласиться с @Angew, что в данном примере нет необходимости в std::function. Конечно, это может быть иначе в реальной ситуации.

Ответ 3

std::function есть шаблонный (и в противном случае безусловный) конструктор, поэтому вывести его из простого типа аргумента не так просто. Если эти аргументы по-прежнему должны быть std::function s, вы можете пропустить один <int> для цены двух std::function s, и пусть гиды-ограничители делают все остальное:

void f() {
    examplFunction(std::function([]() { return 1; }),   //
                   std::function([](int v) { std::cout << "result is "
                                             << v << std::endl; }));
}

Интересным фактом является то, что это не всегда работает. Например, в текущей реализации libc++ отсутствуют руководства для std::function, что нарушает стандарт.

Ответ 4

Угловой ответ велик (и должен быть принятым ответом), но отсутствует незначительная деталь проверки того, что функция раздела ничего не возвращает. Для этого вам нужно использовать std::is_void тип trait и std :: enable_if:

template<class F1, class F2>
void examplFunction(F1 func, F2 func2, std::enable_if_t<std::is_void_v<decltype(func2(func()))>, void*> sfinae = nullptr) {
    auto x = func();
    func2(x);
}

Это очевидное более подробное и трудное для чтения, если вы не знакомы с чертами типа и SFINAE, поэтому, вероятно, это не лучший путь вперед, если вам не нужно убеждаться, что F2 возвращает void.