Home > Mobile >  Type deduction for a member function pointer
Type deduction for a member function pointer

Time:07-10

I know there are similar questions on SO, but the usages there seem different to what I have. Here is my MRE:

#include <iostream>
#include <functional>
using namespace std;

void freeFunction() {}

struct Foo { void memberFunction() {} };

template<typename FunctionPtrT>
void foo(FunctionPtrT* f) { f(); }

template<typename InstanceT, typename FunctionPtrT>
void bar(InstanceT&& i, FunctionPtrT f) { std::mem_fn(f)(i); }

template<typename InstanceT, typename FunctionPtrT>
void baz(InstanceT&& i, FunctionPtrT* f) {}

int main() {
  foo(&freeFunction); //Ok, obviously
  bar(Foo(), &Foo::memberFunction); //Ok, how?!
  // Error: candidate template ignored: could not match 'FunctionPtrT *' against 'void (Foo::*)()'
  baz(Foo(), &Foo::memberFunction); //why?!
  return 0;
}

Why is &Foo::memberFunction not resolving to a pointer? What is the type of f in bar? If it's not a pointer how am I able to pass it to std::mem_fun that is defined as template< class M, class T > /*unspecified*/ mem_fn(M T::* pm) noexcept;?

CodePudding user response:

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_assertion 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)());

CodePudding user response:

What is the type of f in bar?

The type of f in bar is void (Foo::*)() i.e., a pointer to a member function of class Foo that has no parameter and has the return type of void.


The reason baz(Foo(), &Foo::memberFunction); fails is that &Foo::memberFunction is a pointer to a member function is distinct from an ordinary pointer to some type.

In fact, member function pointers are not actually pointers. So the argument of type void (Foo::*)() cannot be passed to parameter FunctionPtrT* f.

  • Related