Search code examples
c++templatesc++17function-pointers

Pointer to member function or to static function in template context


I am using pointer to member function in generic context and It works OK.

struct Mock{
    static int inc(int){
        return 0;
    }

    static int dec(int){
        return 0;
    }
};

struct Real{
    Real(int v) : v(v){}

    int inc(int a) const{
        return a + v;
    }

    int dec(int a) const{
        return a - v;
    }

private:
    int v;
};

template<typename C, typename F>
auto user(C &c, F func){
    return (c.*func)(5);
}

int main(){
    Real real(5);

    return user(real, &Real::inc);
}

However if I try to pass static method (in case of Mock), it stops working, because static method is like normal function.

What I need to change in user function, so this compiles and work properly?

I was able to do it with lambda, but it was way more boilerplate code.

I am thinking of SFINAE or constexpr if, but I am not sure how to detect if method is static.

int main(){
    Mock real;

    return user(real, &Mock::inc);
}

I am using C++17.


Solution

  • std::is_member_pointer can be used to detect pointers to members. You can then do a simple if constexpr to vary the behavior between those and a callable that should accept just the argument.

    template<typename C, typename F>
    auto user(C &c, F func){
        if constexpr (std::is_member_pointer_v<F>)
            return (c.*func)(5);
        else
            return func(5);
    }
    

    Alternatively, if you restructure your Mock a bit

    struct Mock{
        static int inc(Mock const&, int){
            return 0;
        }
    
        static int dec(Mock const&, int){
            return 0;
        }
    };
    

    Then you can simply use std::invoke

    template<typename C, typename F>
    auto user(C &c, F func){
        return std::invoke(func, c, 5);
    }