1

I want to do something like this:

struct S
{
   void mf() {};

   template <auto f>
   void func()
   {
      f();
   }
};

int main()
{
   S x;
   x.func<x.mf>();
}

However, these are the errors:

error: no matching function for call to 'S::func<x.S::mf>()'`
note: candidate: 'template<auto f> void S::func()'
note:   template argument deduction/substitution failed:
error: could not convert 'x.S::mf' from '<unresolved overloaded function type>' to 'void (S::*)()'

I am not sure I understand what I am doing wrong.

Why is x.mf not resolved since I already instantiated x? And how do I make this work?

JeJo
  • 30,635
  • 6
  • 49
  • 88
RexYuan
  • 280
  • 7
  • 13

3 Answers3

3

Calling member functions through a pointer can be so tricky.

You want to pass a member function pointer to S::mf like so:

struct S
{
    void mf () {std::cout << "called mf\n";};

    template <auto f>
    void func ()
    {
        (this->*f)(); 
    }
};

int main()
{
    S x;
    x.func<&S::mf>();
}
AndyG
  • 39,700
  • 8
  • 109
  • 143
  • Thank you! Could you give me some keywords to look up on the particular usage of `(this->*f)` and the need for `&S::mf`? Also I tried to use std::function in place of auto but couldn't make it work. I changed it to this version but still couldn't use std::function. What is f deduced exactly in this case? – RexYuan Sep 13 '20 at 21:29
  • 2
    @RexYuan the keywords you are looking for are "member function pointer" – AndyG Sep 13 '20 at 21:43
1

x.mf is not a type but a member-function pointer. We must pass it as a parameter.

Here is your modified example (not certain about what you want to do).

#include <iostream>

struct S
{
    int i=123;

    void mf () { std::cout << "i=" << i << '\n'; };

    template <typename f>
    void func (f fnct)
    {
        (this->*fnct)();   
    }
};

int main()
{
    S x{456};
    
    x.func(&S::mf);
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • With `auto` templates, we can pass a reference to the function without issue. Without auto, we can still do it, but we have to know the type ahead of time. – AndyG Sep 13 '20 at 21:25
  • @AndyG Ah ah, thank you for the information. I didn't even know that `template ` existed! (I'm serious) – prog-fh Sep 13 '20 at 21:33
  • 2
    Don't be too hard on yourself, it's fairly new syntax – AndyG Sep 13 '20 at 21:44
1

Why is x.mf not resolved since I already instantiated x?

Because it is not a valid syntax. There you need to mention the member function by operator &, meaning you should have

x.func<&S::mf>();
//    ^^^^^^^^

So that the template parameter will be deduced to a corresponding member function pointer. That is void(S::*)().


How do I make this work?

The second problem is, the function call f() should have been a call through a member function pointer. This is different than the normal function call.

The traditional way of calling the member function pointer with the instance is

(this->*f)();

However, since this is more convenient way using more generic liberary function, so calledstd::invoke from <functional> header.

Meaning you could do more readable call through a member function pointer like.

#include <functional> // std::invoke

template <auto f>
void func()
{
   std::invoke(f, this);
}

See a demo

JeJo
  • 30,635
  • 6
  • 49
  • 88