如何使用模板将lambda转换为std :: function

问题描述 投票:62回答:8

[基本上,我想做的是将具有任意数量的任何类型的参数的lambda转换为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 ++中实现currying。不幸的是,这在使用lambda时会严重失败。例如,我可以使用函数指针传递标准函数。

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

但是,我不知道如何将lambda传递给这种可变参数。我对将通用Lambda转换为std :: function感兴趣的原因是,因为我可以执行以下操作,但最终需要我将模板参数明确声明为std :: function,这是我要避免的事情。

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
    foo(std::function<void()>([](){}));
}
c++ function templates c++11 lambda
8个回答
46
投票

如果不显式指定模板参数std::function<T>,则不能将lambda函数对象作为T类型的参数传递。模板类型推导试图将您的lambda函数的类型与std::function<T>匹配,在这种情况下它是做不到的-这些类型是不同的。模板类型推导不考虑类型之间的转换。

可能的话,您可以通过其他方式推断出类型。您可以通过将函数参数包装为identity类型来做到这一点,以便在尝试将lambda与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;
}

您可以对所有lambda类型说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;
}

Display

Inferring the call signature of a lambda or arbitrary callable for "make_function"所示,您可以从其(单个)operator()推断lambda(或具有单个调用签名的任何其他仿函数的调用签名):

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()的函子,也不适用于具有[[templated

operator()的函子或(C ++ 14)多态lambda。这就是bind将其结果类型推迟到最终调用尝试的原因。
可以使用派生,decltype,可变参数模板和一些类型特征来获得所需的lambda 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);

PS:在我的真实情况下,我将这个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

这是我一个月前写的实验。目的是创建一个类似于函子的C ++模板,该模板模仿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使用某些类型特征来处理不同类型的功能实体,包括lambda:]]

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

如您所见,我使用逗号运算符来实现Hasimell语法,但是您可以将其更改为调用运算符以实现目标语法。

您完全可以随意使用代码进行任何操作(检查许可证)。

在C ++ 17中,有构造函数类型推导。因此,您可以为std :: function模板参数保存一些类型。这不是什么都没有,但是要少一些。

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

七年后,也许是当时最简单的解决方案,今天仍然有效。

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

用法

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

也提供Lambda迷信者

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


23
投票

您可以使用专用/回顾性演员表


14
投票

Inferring the call signature of a lambda or arbitrary callable for "make_function"所示,您可以从其(单个)operator()推断lambda(或具有单个调用签名的任何其他仿函数的调用签名):

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;

7
投票
可以使用派生,decltype,可变参数模板和一些类型特征来获得所需的lambda 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); } }


3
投票
不是要使用std::bind实现的

已经

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

3
投票
这可能对您很有趣:https://gist.github.com/Manu343726/94769034179e2c846acc

这是我一个月前写的实验。目的是创建一个类似于函子的C ++模板,该模板模仿Haskell的部分调用闭包,即,当您使用m-n集合调用具有n参数的函数时,自动创建m集合的闭包。

这是该实验可完成的一个示例:


1
投票
在C ++ 17中,有构造函数类型推导。因此,您可以为std :: function模板参数保存一些类型。这不是什么都没有,但是要少一些。

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


1
投票
七年后,也许是当时最简单的解决方案,今天仍然有效。
© www.soinside.com 2019 - 2024. All rights reserved.