70

基本上,我想要做的是获取一个带有任意数量的任意类型参数的 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++ 中实现柯里化。不幸的是,这在使用 lambdas 时非常失败。例如,我可以使用函数指针传递标准函数。

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()>([](){}));
}
4

10 回答 10

51

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参数的类型。

于 2012-11-13T10:46:26.507 回答
28

您可以使用专用/回顾演员表。一旦你有了这样的工具

#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()对所有 lambda 类型说,将它们转换为正确版本的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;
}

展示

于 2014-06-05T19:13:34.297 回答
14

Inferring the call signature of a lambda or absolute callable for "make_function" 所示,您可以从 (single) 推断出 lambda(或任何其他具有单个调用签名的仿函数)的调用签名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) 多态 lambda 的函子。这就是为什么bind将其结果类型的推断推迟到最终的调用尝试。

于 2012-11-13T10:57:17.683 回答
8

可以使用派生、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 对象及其参数保存在一个通用内核对象中,稍后我可以通过虚拟函数按需执行。

     

于 2014-01-21T19:08:50.303 回答
3

currying 不是已经实现了std::bind吗?

auto sum = [](int a, int b){ return a+b; };
auto inc = std::bind( sum, _1, 1 );
assert( inc(1)==2 );
于 2012-11-13T10:28:07.833 回答
3

这对您来说可能很有趣: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

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

您可以完全自由地使用代码做任何您想做的事情(检查许可证)。

于 2014-06-05T19:42:23.770 回答
2

在 C++17 中有构造函数类型推导。因此,您可以为 std::function 模板参数节省一些输入。这不是什么都没有,而是少了一点。

template <typename R, typename...A>
void foo(std::function<R(A...)>) {}
int main() {
   foo(std::function([](){}));
}    
于 2019-02-05T07:20:53.100 回答
1

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

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 ;
于 2019-09-20T10:57:26.617 回答
0

使用具有显式模板参数列表(C++20 特性)的 lambda 表达式,您可以更轻松地编写函数,如下所示:

template <typename F,typename... T>
auto func(F f, T... values) {
    return f(values...);
}
int main() {
    auto result = func([]<typename... T>(T...args){
            return (...*args);
    },1,2,3,6,22);

    std::cout<<result<<"\n";
}
于 2021-10-24T16:51:28.627 回答
-1

std::result_of如果你只使用函数(没有类/结构,因为声明不会像std::function,而且真的很难看),你现在可以让它像:


template <typename Func, typename ...Args>
std::result_of_t<Func(Args...)> func(Func function, Args... args) {
    /// now do your staff here
}
/// usage:
func([](){printf("lambda function\n"});
于 2021-08-08T15:07:47.137 回答