Если вы просто ищете конкретный пример, рассмотрите следующее:
#include <cassert>
#include <type_traits>
#include <iostream>
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<typename V>
class HasMoveForwardMember // (2)
{
template<typename U, void(U::*)(int) = &U::MoveForward>
struct sfinae_impl { };
typedef char true_t;
struct false_t { true_t f[2]; };
static V* make();
template<typename U>
static true_t check(U*, sfinae_impl<U>* = 0);
static false_t check(...);
public:
static bool const value = sizeof(check(make())) == sizeof(true_t);
};
template<typename V, bool HasMember = HasMoveForwardMember<V>::value>
struct MoveForwardDispatcher // (3)
{
static void MoveForward(V& v_, int units) { v_.MoveForward(units); }
};
template<typename V>
struct MoveForwardDispatcher<V, false> // (3)
{
static void MoveForward(V& v_, int units) { NS::MoveForward(v_, units); }
};
template<typename V>
typename std::enable_if<std::is_base_of<NS::vehicle, V>::value>::type // (4)
mover(NS::direction d, V& v_)
{
switch (d)
{
case NS::forward:
MoveForwardDispatcher<V>::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) - это метафункция, которая проверяет существование функции-члена с таким именем с подписью void(V::*)(int)
в данном классе V
. MoveForwardDispatcher
(3) использует эту информацию для вызова функции-члена, если она существует, или возвращается к вызову свободной функции, если ее нет. mover
просто делегирует вызов MoveForward
на MoveForwardDispatcher
(5) .
Код «как опубликовано» будет вызывать Car::MoveForward
(1) , но если эта функция-член удалена, переименована или изменена ее подпись, вместо нее будет вызван NS::MoveForward
.
Также обратите внимание, что, поскольку mover
является шаблоном, необходимо установить проверку SFINAE, чтобы сохранить семантику, позволяющую передавать только объекты, полученные из NS::vehicle
, для v_
(4) . Для демонстрации, если один из комментариев (7) и раскомментирует (8) , mover
будет вызван с объектом типа NonVehicleWithMoveForward
(6) , который мы хотим запретить, несмотря на то, что HasMoveForwardMember<NonVehicleWithMoveForward>::value == true
.
( Примечание : Если ваша стандартная библиотека не поставляется с std::enable_if
и std::is_base_of
, используйте вместо нее варианты std::tr1::
или boost::
.)
Способ, которым обычно используется этот вид кода, состоит в том, чтобы всегда вызывать свободную функцию и реализовывать свободную функцию в терминах чего-то вроде MoveForwardDispatcher
, так что свободная функция просто вызывает переданную функцию-член объекта, если она существует, без необходимости переписывать эту свободную функцию для каждого возможного типа, который может иметь соответствующую функцию-член.