developer tip

템플릿을 사용하여 람다를 std :: function으로 변환하는 방법

optionbox 2020. 11. 28. 09:09
반응형

템플릿을 사용하여 람다를 std :: function으로 변환하는 방법


기본적으로 내가 원하는 것은 모든 유형의 매개 변수로 람다를 가져와 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()>([](){});

나는 저녁 내내 함수와 템플릿을 뒤죽박죽으로했지만 이것을 알아낼 수 없기 때문에 어떤 도움을 주시면 감사하겠습니다.

코멘트에서 언급했듯이, 제가 이것을 시도하는 이유는 가변 템플릿을 사용하여 C ++에서 커링을 구현하려고하기 때문입니다. 불행히도 이것은 람다를 사용할 때 끔찍하게 실패합니다. 예를 들어 함수 포인터를 사용하여 표준 함수를 전달할 수 있습니다.

template <typename R, typename...A>
void foo(R (*f)(A...)) {}
void bar() {}
int main() {
    foo(bar);
}

그러나 이러한 가변 함수에 람다를 전달하는 방법을 알 수 없습니다. 제네릭 람다를 std :: function으로 변환하는 데 관심이있는 이유는 다음을 수행 할 수 있기 때문입니다.하지만 결국에는 템플릿 매개 변수를 std :: function에 명시 적으로 지정해야합니다.

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
    foo(std::function<void()>([](){}));
}

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인수 유형을 추론 할 수 없습니다 .


전담 / 회고 캐스트를 사용할 수 있습니다 . 이런 도구가 있으면

#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;
}

디스플레이


에서와 같이 "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;

그러나 이것은 다소 융통성이없는 접근 방식입니다. R. Martinho Fernandes가 말했듯이 operator()s 가 여러 개인 펑 터나 템플릿이있는 펑터 operator()또는 (C ++ 14) 다형성 람다에 대해서는 작동하지 않습니다 . 이것이 bind최종 호출이 시도 될 때까지 결과 유형의 추론을 연기하는 이유 입니다.


파생, decltype, variadic 템플릿 및 몇 가지 유형 특성을 사용하여 람다에 필요한 std :: function 유형을 얻을 수 있습니다.

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);

추신 : 실제 경우에는이 std :: function 객체와 그 인수를 가상 함수를 통해 나중에 실행할 수있는 일반 커널 객체 안에 저장합니다.

     


커링이 이미 구현되어 std::bind있지 않습니까?

auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );

이것은 당신에게 흥미로울 수 있습니다 : https://gist.github.com/Manu343726/94769034179e2c846acc

한 달 전에 작성한 실험입니다. 목표는 Haskell의 부분 호출 클로저를 에뮬레이트하는 functor와 같은 C ++ 템플릿을 만드는 것이 었습니다. 즉, 인자 매개 변수가 있는 함수 m-n를 호출 할 때 인자 클로저를 자동으로 생성 n하는 것입니다 m.

This is one example of what this experiment is cappable to do:

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 uses some type traits to take care of the different types of function entities, lambdas included:

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

As you can see, I use comma operator to mmimic Haskell syntax, but you could change it to the call operator to achieve your goal syntax.

Your are completely free to do anything you want with the code (Check the license).


In C++17 there is the constructor type deduction. So you can save some typing for the std::function template arguments. This is not quite nothing, but a bit less.

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
   foo(std::function([](){}));
}    

Seven years later and probably the simplest solution then, still works today.

template< char const * (*name) () >
struct user {
  auto id() { return name(); }
} ;

Usage

constexpr auto lama () { return "Lama"; } 

 int main( int , char * [] )
 {
   auto amuser = user< lama >{} ;
   cout << boolalpha << amuser.id() << endl ;
 }

Lambda afficionados are served too

 auto cat = [] () constexpr { return "Cat"; } ;
 auto sneaky = user< cat >{} ;
 cout << boolalpha << sneaky.id() << endl ;

참고URL : https://stackoverflow.com/questions/13358672/how-to-convert-a-lambda-to-an-stdfunction-using-templates

반응형