Как преобразовать лямбда в std:: функцию с помощью шаблонов
В принципе, то, что я хочу сделать, это взять лямбда с любым количеством параметров любого типа и преобразовать его в std:: function.
Я пробовал следующее, и ни один из методов не работает.
std::function([](){});//Complains that std::function is missing template parameters
template <typename T> void foo(function<T> f){}
foo([](){});//Complains that it cannot find a matching candidate
Следующий код действительно работает, но это не то, что я хочу, потому что он требует явно указывать параметры шаблона, которые не работают для общего кода.
std::function<void()>([](){});
Я весь день торчал с функциями и шаблонами, и я просто не могу понять это, поэтому любая помощь будет очень оценена.
Как упоминалось в комментарии, причина, по которой я пытаюсь это сделать, - это то, что я пытаюсь реализовать currying на С++ с использованием вариативных шаблонов. К сожалению, это не удается с помощью лямбда. Например, я могу передать стандартную функцию с помощью указателя функции.
template <typename R, typename...A>
void foo(R (*f)(A...)) {}
void bar() {}
int main() {
foo(bar);
}
Однако я не могу понять, как передать лямбду такой вариационной функции. Почему я заинтересован в преобразовании общей лямбды в функцию std::, потому что я могу сделать следующее, но в конечном итоге это требует, чтобы я явно указывал параметры шаблона в std:: function, чего я пытаюсь избежать.
template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
foo(std::function<void()>([](){}));
}
Ответы
Ответ 1
Вы не можете передать объект лямбда-функции в качестве аргумента типа std::function<T>
без явного указания аргумента шаблона T
. Вычисление типа шаблона пытается сопоставить тип вашей лямбда-функции с std::function<T>
, которую он просто не может сделать в этом случае - эти типы не совпадают. Вычисление типа шаблона не учитывает преобразования между типами.
Возможно, если вы можете дать ему другой способ вывода типа. Вы можете сделать это, обернув аргумент функции в тип identity
, чтобы он не терпел неудачу при попытке сопоставления лямбда с std::function
(поскольку зависимые типы просто игнорируются методом вывода типа) и даются некоторые другие аргументы.
template <typename T>
struct identity
{
typedef T type;
};
template <typename... T>
void func(typename identity<std::function<void(T...)>>::type f, T... values) {
f(values...);
}
int main() {
func([](int x, int y, int z) { std::cout << (x*y*z) << std::endl; }, 3, 6, 8);
return 0;
}
Это явно не полезно в вашей ситуации, потому что вы не хотите передавать значения до более позднего времени.
Поскольку вы не хотите указывать параметры шаблона и не хотите передавать другие аргументы, из которых могут быть выведены параметры шаблона, компилятор не сможет вывести тип вашего аргумента std::function
.
Ответ 2
Вы можете использовать выделенный/ретроспективный листинг. Если у вас есть такой инструмент,
#include <functional>
using namespace std;
template<typename T>
struct memfun_type
{
using type = void;
};
template<typename Ret, typename Class, typename... Args>
struct memfun_type<Ret(Class::*)(Args...) const>
{
using type = std::function<Ret(Args...)>;
};
template<typename F>
typename memfun_type<decltype(&F::operator())>::type
FFL(F const &func)
{ // Function from lambda !
return func;
}
вы можете сказать FFL()
всем типам лямбды , чтобы они были преобразованы в то, что было бы правильной версией std::function
template <typename... Args> void Callback(std::function<void(Args...)> f){
// store f and call later
}
int main()
{
Callback(FFL([](int a, float b){
// do something
}));
return 0;
}
Дисплей
Ответ 3
Как показано на Вывод сигнатуры вызова из лямбда или произвольного вызываемого для "make_function" , вы можете сделать вывод вызывающей сигнатуры лямбда (или любого другого функтора с одной сигнатурой вызова) из своего (одного) operator()
:
template<typename T> struct remove_class { };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...)> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
template<typename C, typename R, typename... A>
struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };
template<typename T>
struct get_signature_impl { using type = typename remove_class<
decltype(&std::remove_reference<T>::type::operator())>::type; };
template<typename R, typename... A>
struct get_signature_impl<R(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
template<typename R, typename... A>
struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
template<typename T> using get_signature = typename get_signature_impl<T>::type;
Это довольно негибкий подход; как говорит Р. Мартиньо Фернандес, он не будет работать для функторов с несколькими operator()
s, а также для функторов с шаблоном operator()
или для (С++ 14) полиморфных лямбда. Вот почему bind
отклоняет вывод своего типа результата до возможной попытки вызова.
Ответ 4
Можно получить необходимый тип std:: function для лямбда, используя деривацию, decltype, variadic templates и несколько типов признаков:
namespace ambient {
template <typename Function>
struct function_traits : public function_traits<decltype(&Function::operator())> {};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
typedef ReturnType (*pointer)(Args...);
typedef const std::function<ReturnType(Args...)> function;
};
template <typename Function>
typename function_traits<Function>::function to_function (Function& lambda) {
return static_cast<typename function_traits<Function>::function>(lambda);
}
template <class L>
struct overload_lambda : L {
overload_lambda(L l) : L(l) {}
template <typename... T>
void operator()(T&& ... values){
// here you can access the target std::function with
to_function(*(L*)this)(std::forward<T>(values)...);
}
};
template <class L>
overload_lambda<L> lambda(L l){
return overload_lambda<L>(l);
}
}
Я использую его в своем коде следующим образом:
ambient::lambda([&](const vector<int>& val){
// some code here //
})(a);
PS: в моем реальном случае я сохраняю этот объект std:: function и его аргументы внутри общих объектов ядра, которые я могу выполнить позже по требованию с помощью виртуальных функций.
Ответ 5
Не выполняется ли уже currying с помощью std::bind
?
auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );
Ответ 6
Это может быть интересно для вас: https://gist.github.com/Manu343726/94769034179e2c846acc
Это эксперимент, который я написал месяц назад. Цель состояла в том, чтобы создать подобный функтору шаблон С++, который эмулирует закрытие частных вызовов Haskell, т.е. Автоматическое создание замыкания аргументов m-n
, когда вы вызываете с помощью n
, аргументирует функцию параметрами m
.
Это один из примеров того, что может сделать этот эксперимент:
int f( int a, int b, int c, int d)
{
return a+b+c+d;
}
int main()
{
auto foo = haskell::make_function( f );
auto a = foo , 1 , 2 , 3; //a is a closure function object with one parameter
std::cout << a , 4 << std::endl; //Prints 10
}
haskell::make_function
использует некоторые черты типа, чтобы позаботиться о различных типах объектов функции, включая lambdas:
auto f = haskell::make_function( []( int x, int y , int z ){ return x*y*z; } );
auto a = f(1,2); //a is functor with one parameter (Using the alternative C++-like syntax)
auto b = a(3); // b is 6
Как вы можете видеть, я использую оператор запятой для синтаксиса mmimic Haskell, но вы можете изменить его на оператор вызова для достижения синтаксиса цели.
Вы можете свободно делать все, что хотите, с кодом (Проверить лицензию).
Ответ 7
В С++ 17 есть дедукция типа конструктора. Таким образом, вы можете сохранить некоторую типизацию для аргументов шаблона std :: function. Это не совсем ничего, но немного меньше.
template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
foo(std::function([](){}));
}
Ответ 8
Семь лет спустя, и, возможно, самое простое решение, все еще работает сегодня.
template< char const * (*name) () >
struct user {
auto id() { return name(); }
} ;
Usage
Usage
constexpr auto lama () { return "Lama"; }
int main( int , char * [] )
{
auto amuser = user< lama >{} ;
cout << boolalpha << amuser.id() << endl ;
}
Лямбда-любители тоже подаются
auto cat = [] () constexpr { return "Cat"; } ;
auto sneaky = user< cat >{} ;
cout << boolalpha << sneaky.id() << endl ;