Can generic lambdas take advantage of the "Substitution Failure Is Not An Error" rule ? Example
auto gL =
[](auto&& func, auto&& param1, auto&&... params)
-> enable_if_t< is_integral<
std::decay_t<decltype(param1)>
>::value>
{
// ...
};
auto gL =
[](auto&& func, auto&& param1, auto&&... params)
-> enable_if_t< !is_integral<
std::decay_t<decltype(param1)>
>::value>
{
// ...
};
Are there any workarounds or plans to include this in the language ? Also since generic lambdas are templated function objects under the hood isn't it a bit odd that this can't be done ?
Lambdas are function objects under the hood. Generic lambdas are function objects with template operator()
s.
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 )
generates an object that is an overload of both f1
and 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 );
and calling gL
will do SFINAE friendly overload resolution on the two lambdas.
The above does some spurious moves, which could be avoided, in the linear inheritance of funcs_t
. In an industrial quality library, I might make the inheritance binary rather than linear (to limit instantiation depth of templates, and the depth of the inheritance tree).
As an aside, there are 4 reasons I know of to SFINAE enable lambdas.
First, with new std::function
, you can overload a function on multiple different callback signatures.
Second, the above trick.
Third, currying a function object where it evaluates when it has the right number and type of args.
Forth, automatic tuple unpacking and similar. If I'm using continuation passing style, I can ask the passed in continuation if it will accept the tuple unpacked, or the future unbundled, etc.