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

ret*_*998 62 c++ lambda templates function c++11

基本上,我希望能够做的是使用任意数量的任何类型的参数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
Run Code Online (Sandbox Code Playgroud)

但是,下面的代码确实有效,但它不是我想要的,因为它需要明确说明对通用代码不起作用的模板参数.

std::function<void()>([](){});
Run Code Online (Sandbox Code Playgroud)

我整个晚上一直在使用功能和模板,我只是想不出来,所以任何帮助都会非常感激.

正如评论中所提到的,我试图这样做的原因是因为我试图使用可变参数模板在C++中实现currying.不幸的是,这在使用lambdas时非常糟糕.例如,我可以使用函数指针传递标准函数.

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

但是,我无法弄清楚如何将lambda传递给这样的可变函数.为什么我对将泛型lambda转换为std :: function感兴趣是因为我可以执行以下操作,但最终要求我明确地将模板参数声明为std :: function,这正是我想要避免的.

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

Jos*_*eld 44

如果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;
}
Run Code Online (Sandbox Code Playgroud)

这在你的情况下显然没用,因为你不想在以后传递这些值.

由于您不想指定模板参数,也不想传递可以推导出模板参数的其他参数,因此编译器将无法推断出std::function参数的类型.


Nik*_*iou 23

您可以使用专门/回顾性演员.一旦你有这样的工具

#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;
}
Run Code Online (Sandbox Code Playgroud)

你可以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;
}
Run Code Online (Sandbox Code Playgroud)

显示

  • 这个FFL的东西很漂亮!更改_`Callback`_的原型:_`template <typename ... Args>`_ _`auto parameter_grinding_callback(std :: function <void(Args ...)> f) - > decltype(f)` _然后可以写:_`auto omg = parameter_grinding_callback(FFL([](int a){std :: cout << a << std :: endl;}));`_ _`omg(5);` _ (2认同)

eca*_*mur 14

如在为"make_function"推断lambda或任意可调用的调用签名时所示,您可以从其(单个)推断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;
Run Code Online (Sandbox Code Playgroud)

不过,这是一种相当不灵活的方法; 正如R. Martinho Fernandes所说,它不适用于具有多个operator()s的算子,也不适用于具有模板化 operator()或(C++ 14)多态lambda的算子.这就是为什么bind推迟其结果类型的推断直到最终的调用尝试.

  • 哦,jeez ......我想我会暂时远离lambdas.他们变得比他们的价值更麻烦. (2认同)

小智 7

可以使用derivation,decltype,variadic模板和一些类型特征为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);
    }

}
Run Code Online (Sandbox Code Playgroud)

我在我的代码中使用它,如下所示:

ambient::lambda([&](const vector<int>& val){ // some code here // })(a);

PS:在我的实际案例中,我将这个std :: function对象及其参数保存在一个通用的内核对象中,我可以通过虚函数随后执行.