简体   繁体   中英

Function template taking a template non-type template parameter

How does one take a templated pointer to a member function?

By templated I mean that the following types are not known in advance:

  • template param T is class of the pointer to member
  • template param R is the return type
  • variadic template param Args... are the parameters

Non-working code to illustrate the issue:

template <???>
void pmf_tparam() {}

// this works, but it's a function parameter, not a template parameter
template <class T, typename R, typename... Args>
void pmf_param(R (T::*pmf)(Args...)) {}    

struct A {
  void f(int) {}
};

int main() {
  pmf_tparam<&A::f>();  // What I'm looking for
  pmf_param(&A::f);     // This works but that's not what I'm looking for
  return 0;
}

Is it possible to achieve the desired behavior in C++11?

I don't think this notation is possible, yet. There is proposal P0127R1 to make this notation possible. The template would be declared something like this:

template <auto P> void pmf_tparam();
// ...
pmf_tparam<&S::member>();
pmf_tparam<&f>();

The proposal to add auto for non-type type parameters was voted into the C++ working paper in Oulu and the result was voted to become the CD leading towards C++17 also in Oulu. Without the auto type for the non-type parameter, you'd need to provide the type of the pointer:

template <typename T, T P> void pmf_tparam();
// ...
pmf_tparam<decltype(&S::member), &S::member>();
pmf_tparam<decltype(&f), &f>();

As you've not said really what you are after in the function, the simplest is:

struct A {
  void bar() {
  }
};

template <typename T>
void foo() {
  // Here T is void (A::*)()
}

int main(void) {
  foo<decltype(&A::bar)>();
}

However if you want the signature broken down, I'm not sure there is a way to resolve the types directly, however you can with a little indirection...

struct A {
    void bar() {
        std::cout << "Call A" << std::endl;    
    }
};

template <typename R, typename C, typename... Args>
struct composer {
    using return_type = R;
    using class_type = C;
    using args_seq = std::tuple<Args...>;
    using pf = R (C::*)(Args...);
};

template <typename C, typename C::pf M>
struct foo {
    static_assert(std::is_same<C, composer<void, A>>::value, "not fp");

    typename C::return_type call(typename C::class_type& inst) {
        return (inst.*M)();
    }

    template <typename... Args>
    typename C::return_type call(typename C::class_type& inst, Args&&... args) {
        return (inst.*M)(std::forward<Args...>(args...));
    }
};

template <class T, typename R, typename... Args>
constexpr auto compute(R (T::*pmf)(Args...)) {
    return composer<R, T, Args...>{};
}

int main() {
   foo<decltype(compute(&A::bar)), &A::bar> f;
   A a;
   f.call(a);
}

The above should do what you are after...

What you can do is

template <template T, T value>
void pmf_tparam() {}

and then

pmf_tparam<decltype(&A::f), &A::f>();

The problem is not knowing the type of the argument and wanting a template argument of that type.

With an additional decltype (still in the templated parameter), this works:

#include <iostream>
using namespace std;

template <typename T, T ptr>
void foo (){
    ptr();
}

void noop() {
    cout << "Hello" << endl;
}

int main() {
    //Here have to use decltype first
    foo<decltype(&noop), noop>();

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM