在幕后,Lambda是函数对象。通用的Lambda是带有模板operator()
的函数对象。
template<class...Fs>
struct funcs_t{};
template<class F0, class...Fs>
struct funcs_t<F0, Fs...>: F0, funcs_t<Fs...> {
funcs_t(F0 f0, Fs... fs):
F0(std::move(f0)),
funcs_t<Fs...>(std::move(fs)...)
{}
using F0::operator();
using funcs_t<Fs...>::operator();
};
template<class F>
struct funcs_t<F>:F {
funcs_t(F f):F(std::move(f)){};
using F::operator();
};
template<class...Fs>
funcs_t< std::decay_t<Fs>... > funcs(Fs&&...fs) {
return {std::forward<Fs>(fs)...};
}
auto f_all = funcs( f1, f2 )
生成一个同时包含 f1
和 f2
两个函数的重载对象。
auto g_integral =
[](auto&& func, auto&& param1, auto&&... params)
-> std::enable_if_t< std::is_integral<
std::decay_t<decltype(param1)>
>{}>
{
};
auto g_not_integral =
[](auto&& func, auto&& param1, auto&&... params)
-> std::enable_if_t< !std::is_integral<
std::decay_t<decltype(param1)>
>{}>
{
};
auto gL = funcs( g_not_integral, g_integral );
并且调用 gL
将对这两个 lambda 进行 SFINAE 友好的重载分辨。
上述内容在 funcs_t
的线性继承中会产生一些无意义的移动操作,可以通过使继承关系变为二叉树而避免这种情况(以限制模板实例化的深度和继承树的深度)。
顺带一提,我知道启用 SFINAE 的 lambda 有四个原因。
第一,使用新的 std::function
可以在多个不同的回调签名上重载函数。
第二,上述技巧。
第三,柯里化功能对象,当它具有正确数量和类型的参数时,它将被求值。
第四,自动元组解包等类似功能。如果我使用 continuation passing style,则可以询问传入的 continuation 是否接受元组解包或未捆绑的 future 等。