[英]implicit convert from lambda to std::function inside variadic template
I want to implement a template function which takes a lambda as argument. 我想实现一个以lambda为参数的模板函数。
#include <functional>
template<typename ... Result> using Fun = std::function<void(Result ...)>;
template<typename ... Result> void yield(Fun<Result ...>&& body) {};
template <typename T>
struct identity {
typedef T type;
};
template<typename ... Result> void yield2(typename identity<Fun<Result ...>>::type && body) {};
int main() {
yield<char>(
Fun<char>(
[](char) -> void {} // 1. success
)
);
yield2<char>(
[](char) -> void {} // 2. success with identify
);
yield<char>(
[](char) -> void {} // 3. fail, seems achievable
);
yield(
[](char) -> void {} // 4. fail, impossible ?
);
return 0;
}
Why case 3 fails ? 案例3为何失败? I already giving the template parameters to the template, so it should be able to deduce the function type and implicit convert the lambda to function 我已经将模板参数提供给模板,因此它应该能够推导出函数类型并将lambda隐式转换为函数
EDIT: 编辑:
Compiler always reduce template parameters from function arguments, can we reverse it ? 编译器总是从函数参数中减少模板参数,我们可以反转吗?
yield<char>(
[](auto&& c) -> void {} // 5. is it possible ?
);
The problem is that you have a variadic template, and it starts trying to see "what else" it can deduce besides your explicit char
argument. 问题是你有一个可变参数模板,它开始尝试查看它除了你的显式char
参数之外还能推断出什么。
If you had a single argument template like so: 如果你有一个像这样的参数模板:
template<typename Result> using Fun = std::function<void(Result)>;
template<typename Result> void yield(Fun<Result>&& body) {};
You'll notice that 你会注意到的
yield<char>(
[](char) -> void {} // 3. fail, seems achievable
);
Has no trouble at all because the entirety of std::function
is deducible. 完全没有问题,因为整个std::function
是可以推断的。
But as soon as we make a variadic template our compilers become unhappy: 但是只要我们制作一个可变参数模板,我们的编译器就会变得不快乐:
template<class... Result> using Fun = std::function<void(Result...)>;
template<class... Result> void yield(Fun<Result...>&& body) {};
This is because, like it or not, the compiler is going to try to deduce more template arguments for Fun<Result...>
given the value that was passed in ([temp.deduct.type]). 这是因为,无论喜欢与否,编译器将尝试为Fun<Result...>
推导出更多模板参数,给定传入的值 ([temp.deduct.type])。
yield2
sidesteps this issue because it places the resulting type into a non-deduced context, but since you explicitly specify the template arguments, it will use only those explicitly specified arguments ( char
) to deduce the type (#1 works for basically the same reason). yield2
回避了这个问题,因为它将结果类型放入非推导的上下文中,但由于您明确指定了模板参数,因此它将仅使用那些显式指定的参数( char
)来推断类型(#1的工作原理基本相同)。
The best workaround I think is your yield2
attempt, however you may also do something like this to prevent the value being passed from participating in type deduction: 我认为最好的解决方法是你的yield2
尝试,但是你也可以这样做以防止从参与类型推导传递的值:
auto fn = &yield<char>;
fn(
[](char) -> void {}
);
Another workaround is to static_cast
your call to yield
to the proper type: (Really I'm just reading through other possible workarounds under "The non-deduced contexts are:" for [temp.deduct.type]) 另一个解决方法是static_cast
你的调用yield
正确的类型:(我真的只是阅读[tempddeduct.type]的“非推断的上下文是:”下的其他可能的解决方法)
using fn_type = void(*)(Fun<char>&&);
static_cast<fn_type>(&yield)(
[](char) -> void {}
);
Edit: Finally, you could write some additional boilerplate templating to make the call look nicer (something approaching your #4). 编辑:最后,您可以编写一些额外的样板模板,以使调用看起来更好(接近您的#4)。 Bear in mind this is an incomplete impl made for example: 请记住,这是一个不完整的impl例如:
The goal of this templating is to detect the lambda's operator()
function and extract its return type and arguments so that we can explicitly specify a Fun
type when we call yield
(extracting the return type is unnecessary since you're only ever using void
): 这个模板的目标是检测lambda的operator()
函数并提取它的返回类型和参数,这样我们可以在调用yield
时显式指定Fun
类型(因为你只使用void
所以不需要提取返回类型) :
First a helper struct that will allow us to detect an immutable lambda's return type and argument types: 首先是一个帮助器结构,它允许我们检测不可变的lambda的返回类型和参数类型:
template<class T>
struct Fun_Type;
template<class C, class Ret, class... Args>
struct Fun_Type<Ret(C::*)(Args...) const>
{
using type = Fun<Args...>;
};
Second, our helper function, call_yield
, that passes on Fun_Type<...>::type
to a call to yield
: 第二,我们的辅助函数call_yield
,它将Fun_Type<...>::type
传递给一个调用yield
:
template<class ImmutableLambda>
void call_yield(ImmutableLambda&& c)
{
using Fun_t = typename Fun_Type<decltype(&ImmutableLambda::operator())>::type;
yield(Fun_t{std::forward<ImmutableLambda>(c)});
}
And now we can call it simply: 现在我们可以简单地称它为:
int main() {
call_yield(
[](char) -> void {}
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.