Адаптеры членов классов (1)
Рассмотрим следующий случай. Пусть имеется контейнер, содержащий объекты типа Object. Допустим, что эти объекты можно классифицировать как удовлетворяющие некоторому предикату и как неудовлетворяющие. Для проведения этой классификации используется функция-предикат bool Test(const Object& obj). Объекты неудовлетворяющие этому предикату могут быть удалены из контейнера следующим образом:
Container.erase(remove(Container.begin (), Container.end(), Test),
Container.end());
Однако возможна ситуаций, в которой требуемый предикат является методом класса:
class Object
{
public:
bool Test(void);
...
};
В этом случае хотелось бы использовать имеющийся предикат, а не переписывать его еще раз в виде функции или отдельного функтора.
Стандартные алгоритмы для вызова переданных им функций, предикатов, критериев сравнения используют следующую форму вызова Predicate(object), в то время как функция класса в данном примере должна вызывать с помощью следующей формы вызова object.Predicate(). Очевидно также, что не имеет смысла передавать алгоритму указатель на член класса в виде &Object::Test так как в этом случае правильной формой вызова было бы object.*Predicate, что тоже не соответствует используемой в алгоритмах форме вызова Predicate(object). Для адаптации описанного случая может быть использован mem_fun_ref_t.
Рассмотрим его определение и реализацию (отметим, что приведенная реализация не является стандартной, в действительности может отличаться, и приведена только для пояснения работа адаптера):
template<class Result, class T>
class mem_fun_ref_t : public unary_function<T,Result>
{
public:
explicit mem_fun_ref_t(Result (T::*Fun)()): Function(Fun) {}
Result operator()(T& x) const
{
return ((x.*Function)());
}
private:
Result(T::*Function)();
};
template<class Result, class T, class Arg>
class mem_fun1_ref_t : public binary_function<T,Arg,Result>
{
public:
explicit mem_fun1_ref_t(Result (T::*Fun)(Arg)): Function(Fun) {}
Result operator()(T& x, A y) const
{
return ((x.*Function)(y));
}
private:
Result(T::*Function)(A y);
};
template<class Result, class T>
class const_mem_fun_ref_t : public unary_function<T,Result>
{
public:
explicit const_mem_fun_ref_t(Result (T::*Fun)() const): Function(Fun) {}
Result operator()(const T& x) const
{
return ((x.*Function)());
}
private:
Result(T::*Function)() const;
};
template<class Result, class T, class Arg>
class const_mem_fun1_ref_t : public binary_function<T,Arg,Result>
{
public:
explicit const_mem_fun1_ref_t(Result (T::*Fun)(Arg) const): Function(Fun) {}
Result operator()(const T& x, A y) const
{
return ((x.*Function)(y));
}
private:
Result(T::*Function)(A y) const;
};
mem_fun_ref_t используется для адаптации методов классов без аргументов в функторы принимающие один аргумент (этим аргументом является объект, к которому будет применен метод), а mem_fun1_ref_t используется для адаптации методов классов с одним аргументом в функторы принимающие два аргумента (первым аргументов является объект, к которому будет применен метод, а вторым аргументом функтора является аргумент передаваемый вызываемому методу класса)
Основная идея заложенная в классе mem_fun_ref_t – конструктору объекта передается указатель на метод класса соответствующего типа (адаптируемый метод), который сохраняется во внутренней переменной; при вызове функтора оператору operator() передается ссылка на объект, к которому должен быть применен адаптируемый метод (вызов функтора выполняется с использование functor(object) формы вызова); внутри operator() вызове преобразуется в вид object.*Function(), собственно вызов адаптируемого метода. Аналогично функционирует класс mem_fun1_ref_t, отличие заключается только в дополнительном параметре передаваемом в функтор и метод класса.
Отличие mem_fun_ref_t и mem_fun1_ref_t от const_mem_fun_ref_t и const_mem_fun1_ref_t заключается в том, что const варианты предназначены для адаптации константных методов классов (методов, которые не изменяют объект), а не const варианты адаптируют неконстантные методы классов (методы, которые изменяют объект).
В STL определена вспомогательная функция для создания объектов mem_fun_ref_t, mem_fun1_ref_t, const_mem_fun_ref_t и const_mem_fun1_ref_t
template<class Result, class T> inline
mem_fun_ref_t<Result,T> mem_fun_ref(Result (T::*p)())
{
return (mem_fun_ref_t<Result,T>(p));
}
template<class Result, class T, class Arg> inline
mem_fun1_ref_t<Result,T,Arg> mem_fun_ref(Result (T::*p)(Arg))
{
return (mem_fun1_ref_t<Result,T,Arg>(p));
}
template<class Result, class T> inline
const_mem_fun_ref_t<Result,T> mem_fun_ref(Result (T::*p)() const)
{
return (const_mem_fun_ref_t<Result,T>(p));
}
template<class Result, class T, class Arg> inline
const_mem_fun1_ref_t<Result,T,Arg> mem_fun_ref(Result (T::*p)(Arg) const)
{
return (const_mem_fun1_ref_t<Result,T,Arg>(p));
}
Обратим внимание на то, что для создания объекта нужного класса может быть использована одна функция, вызов которого имеет вид mem_fun_ref(&Class::method); при этом нет необходимости правильно указывать объект, а также параметры шаблона. Если быть точным, для создания объекта используется не одна функция, а один из четырех перегруженных вариантов.
Теперь фрагмент удаления объектов из списка будет выглядеть следующим образом:
vector<Object> VecOfObjects;
...
VecOfObjects.erase(remove((VecOfObjects.begin(), ListOfObjects.end(),
mem_fun_ref(&Object::test)), ListOfObjects.end());
Ниже приведен пример2 использования адаптеров методов класса:
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
#include <string>
using namespace std;
class Person
{
public:
Person(const string& name, int age) : name(name), age(age) {}
// Константный метода класса без аргументов