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.auto f_all = funcs( f1, f2 )
generates an object that is an overload of bothf1
andf2
.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.
A generic lambda can only have one body, so SFINAE wouldn't be of much use here.
One solution would be to package the call into a class which can store the result and is specialized on a
void
return type, encapsulating thevoid
special handling away from your lambda. With a very little overhead, you can do this using the thread library facilities:If you want to avoid the overhead of
packaged_task
andfuture
, it's easy to write your own version:The use of SFINAE is to remove an overload or a specialization from the candidate set when resolving a given function or template. In your case, we have a lambda - that is a functor with a single
operator()
. There is no overload, so there is no reason to use SFINAE1. The fact that the lambda is generic, which makes itsoperator()
a function template, doesn't change that fact.However, you don't actually need to differentiate between different return types. If
func
returnsvoid
for the given arguments, you can stillreturn
it. You just can't assign it to a temporary. But you don't have to do that either:Just write an
RaiiTimer
whose constructor starts a timer and whose destructor stops it and prints the result. This will work regardless offunc
's return type.If you need something more complicated than that, then this is one of those cases where you should prefer a functor over a lambda.
1Actually, as Yakk points out, SFINAE could still be quite handy to check if your function is callable period, which isn't the problem you're trying to solve - so in this case, still not very helpful.