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

Как преобразовать лямбда в 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()>([](){}));
}
4b9b3361

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