I am tried to create a function overload so it only binds (works for) a member function. I took a look at the function signature of std::mem_fn
http://en.cppreference.com/w/cpp/utility/functional/mem_fn
template <class Ret, class T>
/* unspecified */ mem_fn (Ret T::* pm);
So I structured my parameters as such
template <typename R, typename F>
auto call_me(R C::* func) {
return (mContainer.*func);
}
However, I then get this error
.\template.cpp: In function 'int main()':
.\template.cpp:29:49: error: no matching function for call to 'MyClass<int, std::vector<int> >::call_me(std::vector<int>::size_type (std::vector<int>::*)() const noexcept)'
cout << test.call_me(&std::vector<int>::size) << endl;
^
.\template.cpp:16:10: note: candidate: template<class R, class F> auto MyClass<T, C>::call_me(R C::*) [with R = R; F = F; T = int; C = std::vector<int>]
auto call_me(R C::* func) {
^~~~~~~
.\template.cpp:16:10: note: template argument deduction/substitution failed:
.\template.cpp:29:49: note: couldn't deduce template parameter 'F'
cout << test.call_me(&std::vector<int>::size) << endl;
The reason I am trying to do this is so I can have an overload that works for general lambda and functional objects and another overload that works for member functional pointers. Here is an minimal example of what I am trying to achieve. I know this question is a bit confusing so please feel free to ask for clarification if need be.
#include <vector>
#include <iostream>
using namespace std;
template <typename T, typename C>
struct MyClass {
// This doesnt work because of SFINAE
template <typename F, typename... A>
auto call_me(F func, A... args) { // lambda version
return func(args...);
}
template <typename R, typename F>
auto call_me(R C::* func) { // member function version
return (mContainer.*func);
}
C mContainer; // this is private in my actual code
};
int main() {
MyClass<int, std::vector<int> > test;;
// these two calls will call the member function version of the overload
cout << test.call_me(&std::vector<int>::size) << endl;
using insert_func_t = std::vector<int>::iterator(std::vector<int>::*)(std::vector<int>::const_iterator, const int&);
test.call_me(static_cast<insert_func_t>(&std::vector<int>::insert), test.mContainer.begin(), 4);
// this call will call the lambda version of the overload
cout << test.call_me([](std::vector<int>& in){ in.push_back(5); });
return 0;
}
You can cover both cases with std::invoke
:
template <typename F, typename... A>
auto call_me(F func, A... args) { // lambda version
return std::invoke(func, mContainer, args...);
}
For a function object, like your closure, this calls operator()
. For a member function, it prefixes the arguments with the object to use and then calls it with appropriate syntax. In other words, your work is already done.
You could also take perfect forwarding into account:
template <typename F, typename... A>
auto call_me(F&& func, A&&... args) { // lambda version
return std::invoke(std::forward<F>(func), mContainer, std::forward<A>(args)...);
}