Consider the code:
class Character
{
void kill();
void send_to_wall();
}
template <typename T>
void GeorgeFunc(T fp)
{
??? obj;
(obj.*fp)();
}
int main()
{
GeorgeFunc(&Character::kill);
}
So my question here is: how can I get ???
? It seems that the compiler definitely knows what this type is (Character
) during template instantiation, but I'm not sure how to get at it. My current workaround is to change to: void GeorgeFunc(void (T::*fp)())
, but it would be cleaner to simply get the type from the member function pointer. decltype(fp)
would return void(Character::*)()
, and decltype(fp())
would return void
. Any way to get Character
?
A pointer to a C++ class is done exactly the same way as a pointer to a structure and to access members of a pointer to a class you use the member access operator -> operator, just as you do with pointers to structures. Also as with all pointers, you must initialize the pointer before using it.
A pointer to a member can be declared and used as shown in the following code fragment: typedef int X::*my_pointer_to_member; typedef void (X::*my_pointer_to_function) (int); int main() { my_pointer_to_member ptiptr = &X::a; my_pointer_to_function ptfptr = &X::f; X xobject; xobject.*ptiptr = 10; cout << "The value of a ...
There are two pointer to member operators: . * and ->* . The . * operator is used to dereference pointers to class members.
In C++ , function pointers when dealing with member functions of classes or structs, it is invoked using an object pointer or a this call. We can only call members of that class (or derivatives) using a pointer of that type as they are type safe.
Yes, just use a trait to determine this.
template <typename> struct member_function_traits;
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...)>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
// If you intend to support const member functions you need another specialization.
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...) const>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object const & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
Now your declaration is:
typename member_function_traits<T>::instance_type obj;
However, I would argue that since you require a member function pointer (other types would fail to instantiate due to the line (obj.*fp)()
1) that your function should take a member function pointer directly instead of a completely generic type.
So this definition would not only work, but I would consider it preferred -- the error messages when someone uses something other than a pointer-to-member-function will be much clearer because the argument type will be incompatible:
template <typename Return, typename Object>
void GeorgeFunc(Return (Object::*fp)())
{
Object obj;
(obj.*fp)();
}
Note that this does allow passing a pointer-to-member-function that returns any type. Since we don't really use the return value, we don't care what it is. There is no reason to enforce that it is void
as in your "workaround."
The only downside to using this approach is that you need two overloads if you intend to also accept pointers to member functions that are declared const
. The completely generic implementation does not have this limitation. (I've long wished that pointers to const
member functions were implicitly convertible to pointers to non-const
member functions, but this is currently not allowed by C++.)
1 This isn't 100% true. If you use a completely generic type as you are right now then the caller could theoretically pass a member data pointer instead of a member function pointer. obj.*fp
would evaluate as a reference to the data member, and then you would be invoking operator()()
on it. As long as the data member's type implemented this operator then the template function GeorgeFunc
could be instantiated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With