4

Suppose to have the following definitions

struct Cla {
  void w(int x){}
};

template <typename C, void (C::*m)(int)> void callm(C *c, int args) {}


template <typename C, typename... A, void (C::*m)(A...)>
void callmv(C *c, A &&...args) {}

int main(){
  callm<Cla, &Cla::w>(&cla, 3);
  callmv<Cla, int, &Cla::w>(&cla, 3);
}

The first function (callm) is ok. The second (callmv), however, does not compile, and g++ gives the following error message

test.cpp: In function ‘int main()’:
test.cpp:84:28: error: no matching function for call to ‘callmv<Cla, int, &Cla::w>(Cla*, int)’
   84 |   callmv<Cla, int, &Cla::w>(&cla, 3);
      |   ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
test.cpp:52:6: note: candidate: ‘template<class C, class ... A, void (C::* m)(A ...)> void callmv(C*, A&& ...)’
   52 | void callmv(C *c, A &&...args) {}
      |      ^~~~~~
test.cpp:52:6: note:   template argument deduction/substitution failed:
test.cpp:84:28: error: type/value mismatch at argument 2 in template parameter list for ‘template<class C, class ... A, void (C::* m)(A ...)> void callmv(C*, A&& ...)’
   84 |   callmv<Cla, int, &Cla::w>(&cla, 3);
      |   ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
test.cpp:84:28: note:   expected a type, got ‘&Cla::w’

What is the correct syntax? (I already checked Methods as variadic template arguments )

Fabio Dalla Libera
  • 1,297
  • 1
  • 10
  • 24
  • Are you really limited to C++11? Also, is there an actual reason you want to have the pointer as a template argument instead of a function argument? – user17732522 Nov 30 '22 at 02:20
  • Yes, I am limited to C++11. However, if not, what would the solution be? The reason is that I need to keep the same signature as the called function. – Fabio Dalla Libera Nov 30 '22 at 02:24
  • 1
    With C++17 or later you can just take `m` as `auto` non-type template parameter and then `std::invoke` it in your template. You wouldn't need to care about its type. – user17732522 Nov 30 '22 at 02:25
  • 2
    I don't think there is any way to get the syntax you suggest in C++11. You'll need something like `callmv(&cla, 3)` (and `template `), though you can get rid of the first `Cla`. It can be obtained from `M` or deduced from `&cla` if out after `M m` in the list. – user17732522 Nov 30 '22 at 02:31

1 Answers1

2

All parameters after a variadic parameter pack are always deduced and can never be passed explicitly. &Cla::w is being interpreted as the next type argument in the parameter pack A, not the non-type template argument. The error you get is the compiler complaining about &Cla::w not being a type.

Which means you have to pass the member function first

template <typename M, M m, typename C, typename... A>
void callmv(C* c, A&&... args) {}

callmv<decltype(&Cla::w), &Cla::w>(&cla, 3);

Alternatively, you can wrap the member function

template<typename F, typename... Args>
void callf(F f, Args&&... args)
{
    f(std::forward<Args>(args)...);
}

callf([](Cla* c, int i) { c->w(i); }, &c, 42);
Passer By
  • 19,325
  • 6
  • 49
  • 96