Jorge Rodriguez
Jorge Rodriguez

Reputation: 613

Error calling templated pointer-to-member function with a return type

template<typename T, typename F, typename ...Args>
auto f(F h, Args&&... args) -> decltype(h(args...)) {
    T* t = new T(); // Don't worry, my actual code doesn't do this
    return (t->h)(args...);
}

struct G {
    int g() {
        return 5;
    }
};

int main() {
    int k = f<G>(&G::g);
}

Microsoft's compiler says error C2672: 'f': no matching overloaded function found and error C2893: Failed to specialize function template 'unknown-type f(F,Args &&...)'.

Clang's compiler says note: candidate template ignored: substitution failure [with T = G, F = int (G::*)(), Args = <>]: called object type 'int (G::*)()' is not a function or function pointer and error: no matching function for call to 'f'.

I'm pretty sure int (G::*)() is a function pointer ... ? What am I missing? (All of this worked fine before I added the return type.)

Upvotes: 1

Views: 71

Answers (1)

max66
max66

Reputation: 66240

I'm pretty sure int (G::*)() is a function pointer ... ? What am I missing?

Non exactly: int (G::*)() is a pointer to a non-static method. That isn't exactly the same things and require a little different syntax to call it.

So, instead of

return (t->h)(args...);

you should add a * and call h() as follows

return (t->*h)(args...);
// ........^  add this *

The decltype() is also wrong. If you can use at least C++14, you can avoid it and simply use auto as return type

template <typename T, typename F, typename ...Args>
auto f (F h, Args&&... args) {
    T* t = new T(); 
    return (t->*h)(args...);
}

otherwise, if you must use C++11, you can include <utility> and use std::declval() as follows

template <typename T, typename F, typename ...Args>
auto f(F h, Args&&... args) -> decltype((std::declval<T*>()->*h)(args...)) { 
    T* t = new T(); // .................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    return (t->*h)(args...);
}

But there is another way to write your f() function: deducing the returned type (so avoiding the auto, the decltype() and the std::declval()) and the arguments of h()

You can write f() as follows

template<typename R, typename T, typename ... As1, typename ... As2>
R f(R(T::*h)(As1...), As2 && ... args) {
    T* t = new T();
    return (t->*h)(args...);
}

and you avoid to explicit the G type calling it

int k = f(&G::g);
// .....^^^^^^^^   no more explicit <G> needed

because the T template type is deduced from the argument &G::g.

Upvotes: 2

Related Questions