Loads of C++ libraries, the standard included, allow you to adapt your objects for use in the libraries. The choice is often between a member function or a free function in the
If you're just looking for a concrete example, consider the following:
#include
#include
#include
namespace NS
{
enum direction { forward, backward, left, right };
struct vehicle { virtual ~vehicle() { } };
struct Car : vehicle
{
void MoveForward(int units) // (1)
{
std::cout << "in NS::Car::MoveForward(int)\n";
}
};
void MoveForward(Car& car_, int units)
{
std::cout << "in NS::MoveForward(Car&, int)\n";
}
}
template
class HasMoveForwardMember // (2)
{
template
struct sfinae_impl { };
typedef char true_t;
struct false_t { true_t f[2]; };
static V* make();
template
static true_t check(U*, sfinae_impl* = 0);
static false_t check(...);
public:
static bool const value = sizeof(check(make())) == sizeof(true_t);
};
template::value>
struct MoveForwardDispatcher // (3)
{
static void MoveForward(V& v_, int units) { v_.MoveForward(units); }
};
template
struct MoveForwardDispatcher // (3)
{
static void MoveForward(V& v_, int units) { NS::MoveForward(v_, units); }
};
template
typename std::enable_if::value>::type // (4)
mover(NS::direction d, V& v_)
{
switch (d)
{
case NS::forward:
MoveForwardDispatcher::MoveForward(v_, 1); // (5)
break;
case NS::backward:
// ...
break;
case NS::left:
// ...
break;
case NS::right:
// ...
break;
default:
assert(false);
}
}
struct NonVehicleWithMoveForward { void MoveForward(int) { } }; // (6)
int main()
{
NS::Car v; // (7)
//NonVehicleWithMoveForward v; // (8)
mover(NS::forward, v);
}
HasMoveForwardMember
(2) is a metafunction that checks for the existence of a member function of that name with the signature void(V::*)(int)
in a given class V
. MoveForwardDispatcher
(3) uses this information to call the member function if it exists or falls back to calling a free function if it doesn't. mover
simply delegates the invocation of MoveForward
to MoveForwardDispatcher
(5).
The code as-posted will invoke Car::MoveForward
(1), but if this member function is removed, renamed, or has its signature changed, NS::MoveForward
will be called instead.
Also note that because mover
is a template, a SFINAE check must be put in place to retain the semantics of only allowing objects derived from NS::vehicle
to be passed in for v_
(4). To demonstrate, if one comments out (7) and uncomments (8), mover
will be called with an object of type NonVehicleWithMoveForward
(6), which we want to disallow despite the fact that HasMoveForwardMember
.
(Note: If your standard library does not come with std::enable_if
and std::is_base_of
, use the std::tr1::
or boost::
variants instead as available.)
The way this sort of code is usually used is to always call the free function, and implement the free function in terms of something like MoveForwardDispatcher
such that the free function simply calls the passed in object's member function if it exists, without having to write overloads of that free function for every possible type that may have an appropriate member function.