Disambiguate Overloaded Member Function Pointer Being Passed as Template Parameter

Disambiguate overloaded member function pointer being passed as template parameter

The issue is here:

l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);

For both lines, the compiler doesn't know which foo::func you are referring to. Hence, you have to disambiguate yourself by providing the type information that is missing (i.e., the type of foo:func) through casts:

l.call(static_cast<void (foo::*)(const std::string&)>(&foo::func), "hello");
l.call(static_cast<void (foo::*)(const double )>(&foo::func), 0.5);

Alternatively, you can provide the template arguments that the compiler cannot deduce and that define the type of func:

l.call<void, const std::string&>(&foo::func, "hello");
l.call<void, double >(&foo::func, 0.5);

Notice that you have to use double and not const double above. The reason is that generally double and const double are two different types. However, there's one situation where double and const double are considered as if they were the same type: as function arguments. For instance,

void bar(const double);
void bar(double);

are not two different overloads but are actually the same function.

Get pointer to overloaded function that would be called

There is no way to get the function of an overload-set which would be called with the given arguments, unless you already know its signature.

And if you know, what's the point?

The problem is that for any given arguments, taking into account implicit conversions, references, cv-qualifiers, noexcept, old-style vararg, default arguments, and maybe also literal 0 being a null pointer constant, there are an infinite number of function-signatures which would match. And there is currently no facility for "just" listing all candidates.

decltype for overloaded member function

More "closely", as far as I understand, means you want to specify the function arguments of print. That is, for example, you select int, int, then get back the result-type of Foo{}.print(int{},int{}), and thereafter construct a function pointer from all the available information.

Here is an alias template which does that for you in a general way:

template<typename ... Args>
using ptr_to_print_type = decltype(std::declval<Foo>().print(std::declval<Args>() ...)) (Foo::*)(Args ...);

You could also use std::result_of instead of the std::declval stuff, but I like the latter more.

You can use the above as

func<ptr_to_print_type<int,int> >();

EDIT: as asked for by @JavaLover, which btw seems like an unsuitable name for such horrible C++ shit :-), here is the same as above using std::result_of (untested now tested and wrong):

//------ does not compile for overloaded functions --------
template<typename ... Args>
using ptr_to_print_type = std::result_of_t<decltype(&Foo::print)(Foo, Args ...)> (Foo::*)(Args ...)
//------ does not compile for overloaded functions --------

You could further abstract away the Foo but not the print (unless you're using macros).

unresolved overloaded function type with member function pointer and templates

I have found the mistake. You can't send a member function pointer to a template parameter that is stored in a variable, even a constexpr one.

constexpr auto theMethod = &TestClass::setFoo;

// won't work
call(&Caller::callme<decltype(theMethod), theMethod>);

In a template parameter that is a member function pointer, it is required that it is wrote directly:

constexpr auto theMethod = &TestClass::setFoo;

// won't work
call(&Caller::callme<decltype(theMethod), &TestClass::setFoo>);

My solution was to pass around an std::integral_constant instead of the function pointer directly.

using methodType = std::integral_constant<decltype(&TestClass::setFoo), &TestClass::setFoo>;

call(&Caller::callme<methodType>);

The non-type template parameter of pointer to overloaded member functions with inheritance

I found a workaround fitting my requirements. Hoping it be helpful for anyone in need. I got stuck for days...

The idea is using a function template to match a particular overload, and then pass the correct type to G. A layer of indirection always saves the world.

template <typename T>
auto forward_pmf_with_signature( int(T::*pmf)() ) -> decltype(pmf);

G<decltype(forward_pmf_with_signature(&B::f)), &B::f> {}; // works

G is used without the awareness of A and selects the correct overload, cool.

Now the new problem is that I found G<decltype(forward_pmf_with_signature(&B::f)), &B::f> is redundant and error-prone. It's trivial to use a macro USE_G(&B::f) to simply the code, but it seems to me that it's not easy, or even possible to do the simplification in a semantic way.

generic member function pointer as a template parameter

You could try something like this:

template <typename T, typename R, typename ...Args>
R proxycall(T & obj, R (T::*mf)(Args...), Args &&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}

Usage: proxycall(obj, &hello::f);

Alternatively, to make the PTMF into a template argument, try specialization:

template <typename T, T> struct proxy;

template <typename T, typename R, typename ...Args, R (T::*mf)(Args...)>
struct proxy<R (T::*)(Args...), mf>
{
static R call(T & obj, Args &&... args)
{
return (obj.*mf)(std::forward<Args>(args)...);
}
};

Usage:

hello obj;

proxy<void(hello::*)(), &hello::f>::call(obj);

// or

typedef proxy<void(hello::*)(), &hello::f> hello_proxy;
hello_proxy::call(obj);

How do I specify a pointer to an overloaded function?

You can use static_cast<>() to specify which f to use according to the function signature implied by the function pointer type:

// Uses the void f(char c); overload
std::for_each(s.begin(), s.end(), static_cast<void (*)(char)>(&f));
// Uses the void f(int i); overload
std::for_each(s.begin(), s.end(), static_cast<void (*)(int)>(&f));

Or, you can also do this:

// The compiler will figure out which f to use according to
// the function pointer declaration.
void (*fpc)(char) = &f;
std::for_each(s.begin(), s.end(), fpc); // Uses the void f(char c); overload
void (*fpi)(int) = &f;
std::for_each(s.begin(), s.end(), fpi); // Uses the void f(int i); overload

If f is a member function, then you need to use mem_fun, or for your case, use the solution presented in this Dr. Dobb's article.

How can I have a function pointer template as a template parameter?

C++ doesn't allow non-type template template parameters. That means you can't have a parameter-pack for your member-function pointer parameter.

Assuming you're using C++17 or newer, you can use an auto template parameter instead:

template<typename T, auto func>
public:
template<typename... Args>
void update(Args... args)
{
for (T* handler : handlers_)
{
(handler->*func)(std::forward<Args>(args)...);
}
}
private:
std::vector<T*> handlers_;
};

Live Demo

Technically that will accept any object for func, but assuming update is called, then (handler->*func)(std::forward<Args>(args)...) still has to be well-formed or compilation will fail.

If you want compilation to fail even if update never gets called, you could use some type traits and a static_assert (or some SFINAE hackery, if you need it) to ensure that func is actually a pointer to a member function of T:

template <typename T, typename U>
struct IsPointerToMemberOf : std::false_type {};

template <typename T, typename U>
struct IsPointerToMemberOf<T, U T::*> : std::true_type {};

template <typename T, typename U>
struct IsPointerToMemberFunctionOf
: std::integral_constant<
bool,
IsPointerToMemberOf<T, U>::value && std::is_member_function_pointer<U>::value
>
{};

template<typename T, auto func>
class EngineSystem
{
static_assert(IsPointerToMemberFunctionOf<T, decltype(func)>::value, "func must be a pointer to a member function of T");
//...
};

Live Demo



Related Topics



Leave a reply



Submit