Вывод шаблона С++ из лямбда
У меня есть функция, которая принимает две аргумента 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
.