I'd like to have a function that takes a generic lambda, but I need slightly different logic based on the lambda's return type. For example, I'd like to do something like the following (this is a contrived example):
template<typename F>
void foo(F &&function) {
auto result = function();
// Have different behavior based on `result`'s type.
}
To facilitate this, I thought I could use SFINAE. In my mind, this would look something like:
template<typename F,
typename std::enable_if_t<???>>
void foo(F &&function) {
}
Does anyone know how I can enable/disable a method based on the return type of F
?
You don't need SFINAE for this. As mentioned in a comment, you could just write the different logic based on the return type in different overloads, and call that overload set
void f(int); // do the logic for an int
void f(double); // do the logic for a double
// ... and so on
template<typename F>
void foo(F &&function)
{
auto result = function();
f(result); // get different behavior based on `result`'s type.
}
If the logic needs some data other than the return value, you'll need to pass that to the helper f
.
To avoid that, and have all the logic in one place, you could write a compile time "switch" on the type of the return value
template<typename F>
void foo(F &&function)
{
auto result = function();
if constexpr (std::is_same_v<decltype(result), int>)
// logic for an int
else if constexpr (std::is_same_v<decltype(result), double>)
// logic for a double
else
// ... and so on
}
Note that these two options are not exactly equivalent, i.e. overload resolution may not behave exactly the same as is_same
in all cases, due to factors like implicit conversions, etc. so you have to be a bit careful there. One fix would be to write an overload for the first version that catches everything other than exact conversions
template<typename T>
void f(T) = delete;
and this will be much closer to the second version.