Search code examples
c++templates

C++ pass member function of class template as argument


I've got a class template in which I want to pass a function as an argument to another member function

MyClass.h:

template<typename CT>
class MyClass {
public:
  double linfun(double x) { return 0.5 * x + 3.; }
  virtual double some_method();
  virtual double solver(std::function<double(double)> f, double k);
}

MyClass.cpp:

template<typename CT>
double MyClass<CT>::some_method() {
  // do some calculations ...
  x = 42.;
  // Call solver function with linfun as argument:
  return solver(linfun, x);
}

template<typename CT>
double MyClass<CT>::solver(std::function<double(double)> f, double k) { 
  return f(k); 
}

Problem/Error

This gives me the error:

nonstandard form for taking the address of a member function  
    return solver(linfun, x);  
                  ^
no suitable constructor exists to convert from "double (double)" to "std::function<double (double)>"
    return solver(linfun, x);
                  ^
          detected during instantiation of "void MyClass<CT>::some_method() [with CT=SomeClassType]" at line 1234 of "SomethingElse.C"

What I tried:

I tried all proposed solutions I found, f.i.:

return solver(this->*linfun, x);

-> Same error

Next with this:

return solver(this->linfun, x);

With the following error:

error: a pointer to a bound function may only be used to call the function
    return solver(this->linfun, x);
                        ^

Any idea how to make this possible? I'm not restricted to using std::function, but I'd prefer so.


Solution

  • You can't call a non-static member function without an object, so you can't just convert it to a std::function.

    There are several other suggestions you could use.

    1. Make the function static or a free function (the "minimal change" option - the function uses no class members).

    2. Use a lambda object (the "modern" option when you need a non-static member).

    return solver([this](double y) { return linfun(y); } , x);
    
    1. Use an actual "pointer-to-member-function" parameter (the "quickly becomes unreadable and only works with non-static member functions of this particular class" option):
    template<typename CT>
    class MyClass {
    public:
      double linfun(double x) { return 0.5 * x + 3.; }
      virtual double some_method();
      double solver(double (MyClass::*)(double) f, double k) { return this->*f(k); };
    
    // ...
    template<typename CT>
    double MyClass<CT>::some_method() {
      // do some calculations ...
      x = 42.;
      // Call solver function with linfun as argument:
      return solver(&linfun, x);
    }