用于将任何lambda函数(包括捕获lambda)转换为std :: function对象的模板 [英] Template to convert any lambda function (including capturing lambdas) to a std::function object

查看:420
本文介绍了用于将任何lambda函数(包括捕获lambda)转换为std :: function对象的模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码可以将lambda转换为C样式的函数指针.这适用于所有lambda,包括具有捕获功能的lambda.

I have the following code that can convert a lambda into a C-style function pointer. This works for all lambdas including lambdas with captures.

#include <iostream>
#include <type_traits>
#include <utility>

template <typename Lambda>
struct lambda_traits : lambda_traits<decltype(&Lambda::operator())>
{};

template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<Return(Lambda::*)(Args...)> : lambda_traits<Return(Lambda::*)(Args...) const>
{};

template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<Return(Lambda::*)(Args...) const>
{
    using pointer = typename std::add_pointer<Return(Args...)>::type;

    static pointer to_pointer(Lambda&& lambda)
    {
        static Lambda static_lambda = std::forward<Lambda>(lambda);
        return [](Args... args){
            return static_lambda(std::forward<Args>(args)...);
        };
    }
};

template <typename Lambda>
inline typename lambda_traits<Lambda>::pointer to_pointer(Lambda&& lambda)
{
    return lambda_traits<Lambda>::to_pointer(std::forward<Lambda>(lambda));
}

可以使用以下方法将带有捕获的lambda传递到C风格的API中:

This can be used as follows to pass a lambda with a capture into a C-style API:


// Function that takes a C-style function pointer as an argument
void call_function(void(*function)())
{
    (*function)();
}

int main()
{
    int x = 42;

    // Pass the lambda to the C-style API
    // This works even though the lambda captures 'x'!
    call_function(to_pointer([x] {
        std::cout << x << std::endl;
        }));
}

鉴于此,编写一个类似的模板似乎可以相对简单,该模板可以将lambda(包括带有捕获的lambda)一般地转换为std::function对象,但是我一直在努力寻找方法. (我对模板元编程技术不是很熟悉,所以我有点迷茫)

Given this, it seems like it should be relatively straightforward to write a similar template that can convert lambdas (including lambdas with captures) generically into std::function objects, but I am struggling to figure out how. (I am not super familiar with template meta-programming techniques so I am a bit lost)

这是我尝试过的,但是无法编译:

This is what I tried, but it fails to compile:

template <typename Lambda>
struct lambda_traits : lambda_traits<decltype(&Lambda::operator())>
{};

template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<typename std::function<Return(Args...)>> : lambda_traits<typename std::function<Return(Args...)> const>
{};

template <typename Lambda, typename Return, typename... Args>
struct lambda_traits<typename std::function<Return(Args...)> const>
{
    using pointer = typename std::function<Return(Args...)>*;

    static pointer to_pointer(Lambda&& lambda)
    {
        static Lambda static_lambda = std::forward<Lambda>(lambda);
        return [](Args... args) {
            return static_lambda(std::forward<Args>(args)...);
        };
    }
};

template <typename Lambda>
inline typename lambda_traits<Lambda>::pointer to_pointer(Lambda&& lambda)
{
    return lambda_traits<Lambda>::to_pointer(std::forward<Lambda>(lambda));
}

无法编译,并表示部分专业化没有使用Lambda模板参数.

This fails to compile and says that the Lambda template parameter is not being used by the partial specialization.

正确的方法是什么?

(注意,我被困在使用C ++ 11兼容的编译器,因此C ++ 14及更高版本的功能不可用)

推荐答案

如果要在不指定std::function签名的情况下将可调用对象转换为std::function,则正是

If you want to convert a callable object to a std::function without specifying the signature of the std::function, this is exactly what C++17's deduction guides for std::function are for. We just need to implement a version of that for C++11. Note that this only works for callables with a non-overloaded operator(); otherwise, there is no way to do this.

#include <functional>
#include <utility> // std::declval

// Using these functions just for the return types, so they don't need an implementation.

// Support function pointers
template <typename R, typename... ArgTypes>
auto deduce_std_function(R(*)(ArgTypes...)) -> std::function<R(ArgTypes...)>;

// Support callables (note the _impl on the name).
// Many overloads of this to support different const qualifiers and
// ref qualifiers. Technically should also support volatile, but that
// doubles the number of overloads and isn't needed for this illustration.
template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...)) -> std::function<R(ArgTypes...)>;

template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...) const) -> std::function<R(ArgTypes...)>;

template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...) &) -> std::function<R(ArgTypes...)>;

template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...) const&) -> std::function<R(ArgTypes...)>;

template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...) &&) -> std::function<R(ArgTypes...)>;

template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...) const&&) -> std::function<R(ArgTypes...)>;

// To deduce the function type for a callable, get its operator() and pass that to
// the _impl functions above.
template <typename Function>
auto deduce_std_function(Function)
    -> decltype(deduce_std_function_impl(&Function::operator()));

template <typename Function>
using deduce_std_function_t = decltype(deduce_std_function(std::declval<Function>()));

template <typename F>
auto to_std_function(F&& fn) -> deduce_std_function_t<F> {
    return deduce_std_function_t<F>(std::forward<F>(fn));
}

演示

我们需要推断std::function<...>的函数类型.因此,我们需要实现某种deduce_std_function来找出函数类型.有几种方法可以实现此目的:

We need to deduce the function type for the std::function<...>. So we need to implement some kind of deduce_std_function that figures out the function type. There are several options for implementing this:

  • 创建一个function_traits类型,为我们找出功能类型(类似于您的lambda_traits).
  • 实现deduce_std_function作为重载集,其中重载的返回类型是推导类型.
  • Make a function_traits type which figures out the function type for us (similar to your lambda_traits).
  • Implement deduce_std_function as an overload set, where the return type of the overloads is the deduced type.

我之所以选择后者,是因为它模仿了演绎指南.前者也可以,但是我认为这种方法可能更简单(函数样板比结构样板小).

I chose the latter because it mimics deduction guides. The former would work too, but I thought that this method might be easier (function boilerplate is smaller than struct boilerplate).

查看std::function的演绎指南的文档,有一个简单的方法:

Looking at the documentation for std::function's deduction guides, there is an easy one:

template<class R, class... ArgTypes>
function(R(*)(ArgTypes...)) -> function<R(ArgTypes...)>;

这很容易翻译:

template <typename R, typename... ArgTypes>
auto deduce_std_function(R(*)(ArgTypes...)) -> std::function<R(ArgTypes...)>;

基本上,给定任何函数指针R(*)(ArgTypes...),我们想要的类型是std::function<R(ArgTypes...)>.

Basically, given any function pointer R(*)(ArgTypes...), the type we want is std::function<R(ArgTypes...)>.

文档指出第二种情况:

此重载仅在以下情况下参与重载解析 当&F::operator()被视为未经评估的操作数时,其格式正确 并且decltype(&F::operator())的格式为R(G::*)(A...)(可选 cv限定,可选地,noexcept,可选地,左值引用 合格的)用于某些类类型G.推导的类型是 std::function<R(A...)>.

This overload only participates in overload resolution if &F::operator() is well-formed when treated as an unevaluated operand and decltype(&F::operator()) is of the form R(G::*)(A...) (optionally cv-qualified, optionally noexcept, optionally lvalue reference qualified) for some class type G. The deduced type is std::function<R(A...)>.

那是一口.但是,这里的关键思想是碎片:

That's a mouthful. However, the key idea here is the pieces:

  • " decltype(&F::operator())的格式为R(G::*)(A...)"
  • "推导类型为std::function<R(A...)>"
  • "decltype(&F::operator()) is of the form R(G::*)(A...)"
  • "The deduced type is std::function<R(A...)>"

这意味着我们需要获取operator()的指针到成员函数,并将该指针到成员函数的签名用作std::function的签名.

This means that we need to get the pointer-to-member-function for operator() and use the signature of that pointer-to-member-function as the signature of the std::function.

这是从哪里来的:

template <typename Function>
auto deduce_std_function(Function)
    -> decltype(deduce_std_function_impl(&Function::operator()));

我们委托给deduce_std_function_impl,因为我们需要推断出指向成员函数指针&Function::operator()的签名.

We delegate to deduce_std_function_impl because we need to deduce the signature for the pointer-to-member-function &Function::operator().

该impl函数有趣的重载是:

The interesting overload of that impl function is:

template <typename F, typename R, typename... ArgTypes>
auto deduce_std_function_impl(R(F::*)(ArgTypes...)) -> std::function<R(ArgTypes...)>;

简而言之,我们获取了指向成员函数指针的签名(R ... (ArgTypes...)位),并将其用于std::function.语法的其余部分((F::*)位)仅是指向成员函数指针的语法. R(F::*)(ArgTypes...)是类F的成员函数指针的类型,其签名为R(ArgTypes...),并且没有const,volatile或引用限定符.

In short, we're grabbing the signature (the R ... (ArgTypes...) bit) of the pointer-to-member-function and using that for std::function. The remaining part of the syntax (the (F::*) bit) is just the syntax for a pointer-to-member-function. R(F::*)(ArgTypes...) is the type of a pointer-to-member-function for class F with the signature R(ArgTypes...) and no const, volatile, or reference qualifiers.

但是等等!我们要支持const和reference限定符(您可能还希望增加对volatile的支持).因此,我们需要对每个限定符重复上面的deduce_std_function_impl:

But wait! We want to support const and reference qualifiers (you may wish to add support for volatile too). So we need to duplicate the deduce_std_function_impl above, once for each qualifier:

<身体>
签名 类声明
R(F::*)(ArgTypes...) void operator()();
R(F::*)(ArgTypes...) const void operator()() const;
R(F::*)(ArgTypes...) & void operator()() &;
R(F::*)(ArgTypes...) const& void operator()() const&;
R(F::*)(ArgTypes...) && void operator()() &&;
R(F::*)(ArgTypes...) const&& void operator()() const&&;
Signature Class Declaration
R(F::*)(ArgTypes...) void operator()();
R(F::*)(ArgTypes...) const void operator()() const;
R(F::*)(ArgTypes...) & void operator()() &;
R(F::*)(ArgTypes...) const& void operator()() const&;
R(F::*)(ArgTypes...) && void operator()() &&;
R(F::*)(ArgTypes...) const&& void operator()() const&&;

这篇关于用于将任何lambda函数(包括捕获lambda)转换为std :: function对象的模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆