Search code examples
c++templatespointer-to-membermember-functions

Member function pointer as template arguement failed on inherited member functions, how and why?


As the following example v0::test(), we want to write a template function to invoke all class C's member funtions using class C's member function pointer as template arguement, but this way failed on inherited member functions from parients. To ease those failures, we have to add an overload for every base class(see v1::test()), and in some more complicated case its even difficult to write the base class name such as std:tuple, or change the whole function design to v2...v4. Is there any better way?

struct A {
  void fa() {}
};
struct B : A {
  void fb() {}
};
struct C : B {
  void fc() {}
};

namespace v0 {
using c_mem_fn = void (C::*)();
template <c_mem_fn fn>
void invoke_c_mem_fn(C* c) {
  (c->*fn)();
}

void test() {
  C cc;
  C* c = &cc;
  invoke_c_mem_fn<&C::fc>(c);
  //  invoke_c_mem_fn<&B::fb>(c); // compile error
  //  invoke_c_mem_fn<&A::fa>(c); // compile error
  //  invoke_c_mem_fn<&C::fb>(c); // compile error
  //  invoke_c_mem_fn<&C::fa>(c); // compile error
}
} // namespace v0

namespace v1 {
using c_mem_fn = void (C::*)();
template <c_mem_fn fn>
void invoke_c_mem_fn(C* c) {
  (c->*fn)();
}

using b_mem_fn = void (B::*)();
template <b_mem_fn fn>
void invoke_c_mem_fn(C* c) {
  (c->*fn)();
}

template <void (A::*fn)()>
void invoke_c_mem_fn(C* c) {
  (c->*fn)();
}

void test() {
  C cc;
  C* c = &cc;
  invoke_c_mem_fn<&C::fc>(c);
  invoke_c_mem_fn<&B::fb>(c);
  invoke_c_mem_fn<&A::fa>(c);
  invoke_c_mem_fn<&C::fb>(c);
  invoke_c_mem_fn<&C::fa>(c);
}
} // namespace v1

namespace v2 {
template <typename Fn, Fn fn>
void invoke_c_mem_fn(C* c) {
  (c->*fn)();
}

void test() {
  C cc;
  C* c = &cc;
  invoke_c_mem_fn<decltype(&C::fc), &C::fc>(c);
  invoke_c_mem_fn<decltype(&B::fb), &B::fb>(c);
  invoke_c_mem_fn<decltype(&A::fa), &A::fa>(c);
  invoke_c_mem_fn<decltype(&C::fb), &C::fb>(c);
  invoke_c_mem_fn<decltype(&C::fa), &C::fa>(c);
}
} // namespace v2

namespace v3 {
template <typename Fn>
void invoke_c_mem_fn(Fn fn, C* c) {
  (c->*fn)();
}

void test() {
  C cc;
  C* c = &cc;
  invoke_c_mem_fn(&C::fc, c);
  invoke_c_mem_fn(&B::fb, c);
  invoke_c_mem_fn(&A::fa, c);
  invoke_c_mem_fn(&C::fb, c);
  invoke_c_mem_fn(&C::fa, c);
}
} // namespace v3

namespace v4 {
template <typename X, void (X::*fn)()>
void invoke_c_mem_fn(C* c) {
  (c->*fn)();
}

void test() {
  C cc;
  C* c = &cc;
  invoke_c_mem_fn<C, &C::fc>(c);
  invoke_c_mem_fn<B, &B::fb>(c);
  invoke_c_mem_fn<A, &A::fa>(c);
  //  invoke_c_mem_fn<C, &C::fb>(c); // compiler error
  //  invoke_c_mem_fn<C, &C::fa>(c); // compiler error
  invoke_c_mem_fn<B, &C::fb>(c);
  invoke_c_mem_fn<A, &C::fa>(c);
}
} // namespace v4

int main() {
  v1::test();
  v2::test();
  v3::test();
  v4::test();
}

Solution

  • I finally found the c++17 syntax template <auto> in v5::test(), this solved my problem, thanks you all for reading and answering.

    namespace v5 {
    template <auto fn>
    void invoke_c_mem_fn(C* c) {
      (c->*fn)();
    }
    void test() {
      C cc;
      C* c = &cc;
      invoke_c_mem_fn<&C::fc>(c);
      invoke_c_mem_fn<&B::fb>(c);
      invoke_c_mem_fn<&A::fa>(c);
      invoke_c_mem_fn<&C::fb>(c);
      invoke_c_mem_fn<&C::fa>(c);
    }
    } // namespace v5