用于将任何lambda函数(包括捕获lambda)转换为std :: function对象的模板 [英] Template to convert any lambda function (including capturing lambdas) to a std::function object
问题描述
我有以下代码可以将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 yourlambda_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 anddecltype(&F::operator())
is of the formR(G::*)(A...)
(optionally cv-qualified, optionally noexcept, optionally lvalue reference qualified) for some class type G. The deduced type isstd::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 formR(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屋!