General suggestion for type investigations
I think the easiest way to answer questions like "what is the type of x
in this context" is to put a static_assert(std::is_same_v<void, delctype(x)>);
in that same context. The compiler will then tell you the static_assert
ion failed because void
is not equal to the type of x
, revealing what that type is.
is &Foo::memberFunction
a pointer?
And there are variations of that.
For instance, if you truly want to know wheter &Foo::memberFunction
is a pointer, then ask it this way:
static_assert(std::is_pointer_v<decltype(&Foo::memberFunction)>);
and the compiler will tell you something along the lines of
Static_assert failed due to requirement 'std::is_pointer_v<void (Foo::*)()>' [static_assert_requirement_failed]
so it just can't match against FunctionPtrT*
.
As regards
What is the type of f
in bar
?
if you put
static_assert(std::is_same_v<void, decltype(f)>);
inside of bar
, the compiler will tell you
Static_assert failed due to requirement 'std::is_same_v<void, void (Foo::*)()>'
meaning that the type of f
is void(Foo::*)()
.
How does &Foo::memberFunction
's type from a free function pointer?
Compare this with the error you get if you define
void f();
and assert
static_assert(std::is_same_v<void, decltype(f)>);
The compiiler will tell you
Static_assert failed due to requirement 'std::is_same_v<void, void (*)()>'
So the point is that void(*)()
, a pointer to a free function from void
to void
, is different from void (Foo::*)()
, a pointer member of Foo
function from void
to void
.
How can I restrict a template to be instatiated only for member functions?
If you want your template to match only member functions, can go for
template<typename InstanceT, typename R, typename C>
void baz(InstanceT&& i, R C::* f) { }
or, if you want it to match any member function of Foo
, you can clearly either change the order of the template parameters such that you can provide C
manually
template<typename C, typename R, typename InstanceT>
void baz(InstanceT&& i, R C::* f) { }
which allows you to call baz<Foo>(/* args */);
, or you can simply hardcode Foo
in place of C
:
template<typename R, typename InstanceT>
void baz(InstanceT&& i, R Foo::* f) { }
And what if I want to restrict the match to member function with a specific signature?
As you see, we already have a placeholder R
, for the return type.
If we also wanted to have a placeholder for the arguments, we could go for this:
template<typename R, typename C, typename ...Args>
void baz(R (C::*f)(Args...));
Which will just match the same function as the previous example. I showed this just to make you see that, if you want to type the arguments out, you must put parenthesis around C::*f
, or just C::*
, if you want to omit the name (the lack of parenthesis is the typo in the comment under your question). Therefore, if you want to restrict the match to a specific signature, let's say void()
, this is how you do it:
template<typename C>
void baz(void (C::*f)());