Reputation: 1134
I am trying to understand how SFINAE works.
My needs:
I have a set of types which may provide or not a member function called activate
. The thing is if activate
does exist for the given type, it may accept any arguments. I want to write a function that will forward to the type's activate
, if it exists, otherwise simply return true
.
I tried:
#include <iostream>
#include <utility>
#include <functional>
struct A
{
bool activate()
{
std::clog << "A::activate() called" << std::endl;
return true;
}
bool activate(const char*)
{
std::clog << "A::activate(const char*) called" << std::endl;
return true;
}
};
struct B
{
// NOTHING
};
template<class S, class... ARGS>
auto activate(S& s, ARGS&&... p_args) -> typename std::invoke_result<decltype(&S::activate)(S, ARGS...)>::type
{
return s.activate(std::forward<ARGS>(p_args)...);
}
bool activate(...)
{
return true;
}
int main(int argc, char* argv[])
{
A a;
B b;
activate(a);
activate(a, "Hello");
activate(b);
return 0;
}
But nothing is ever printed. Why?
Upvotes: 2
Views: 666
Reputation: 303107
Your problem is here:
template<class S, class... ARGS>
auto activate(S& s, ARGS&&... p_args)
-> typename std::invoke_result<decltype(&S::activate)(S, ARGS...)>::type
// ^^^^^^^^^^^^
If activate
is an overloaded member function, as it is for A
, you can't take its address via decltype
. Secondly, that's an incorrect use of invoke_result
- it doesn't use the result_of
syntax of F(Args...)
, you just do invoke_result<F, Args...>
. It's simpler.
What you want is to just directly invoke it yourself:
template<class S, class... ARGS>
auto activate(S& s, ARGS&&... p_args)
-> decltype(s.activate(std::forward<ARGS>(p_args)...))
This side-steps the question of overloaded names, and how to use invoke_result
.
Upvotes: 4