#c #functor
#c #Функтор
Вопрос:
предположим, у вас есть два (или более) класса с закрытыми векторами-членами:
class A {
private:
std::vector<X> priv_vec;
public:
//more stuff
}
class B {
private:
std::vector<Y> priv_vec;
public:
//more stuff
}
и у вас есть класс-функтор, который имеет состояние и работает с общим вектором (выполняет сортировку или подсчитывает элементы или что-то в этом роде). Состояние функтора инициализируется первым вектором, над которым работает функтор. Если функтор позже будет применен к другому вектору, он изменит свое поведение в зависимости от состояния (сортирует таким же образом или обрезает второй вектор после такого же количества элементов, как и первый, и т.д.)
Каков наилучший способ реализовать такой функтор (шаблон дизайна или функциональный интерфейс?), не предоставляя частные векторы другим классам или пользователю классов?
например: пользователь хотел бы инициализировать этот функтор объектом класса A, а затем использовать этот инициализированный функтор для одного или нескольких объектов класса B. Пользователь не может (и не должен) использовать частные векторы непосредственно в качестве аргументов функции для функтора.
Комментарии:
1. ооо, векторы не обязательно должны содержать один и тот же тип в разных классах (не показано в сообщении)
2. Это не имеет значения для рассматриваемой проблемы, это просто означает, что функция-член functor
operator()(...)
должна быть шаблонной функцией-членом (класс functor не обязательно должен быть шаблонным).
Ответ №1:
Хум, во-первых, будьте осторожны с состояниями в функторах.
Большинство STL-реализаций алгоритмов могут копировать ваши функторы по кругу, поэтому вам обычно приходится извлекать состояние во внешней структуре.
Теперь, что касается применения функторов, ну, это просто: пусть ваши классы объявят шаблонную функцию-член!
class A
{
public:
template <class Functor>
Functor Apply(Functor f);
private:
};
class B
{
public:
template <class Functor>
Functor Apply(Functor f);
};
// Usage
MyFunctor f;
A a;
B b;
b.Apply(a.Apply(f));
Что касается функтора, если вам нужно состояние:
// Alternative 1
class FunctorState {};
class Functor
{
public:
Functor(FunctorStateamp; state): m_state(state) {}
// some operator()(...)
private:
FunctorStateamp; m_state;
};
// Alternative 2
class Functor
{
struct FunctorState {};
public:
Functor(): m_state(new FunctorState) {}
// some operator()(...)
private:
boost::shared_ptr<FunctorState> m_state;
};
Таким образом, Functor
все копии указывают на один и тот же FunctorState
экземпляр. Просто выберите в зависимости от того, хотите ли вы на самом деле получить доступ к состоянию извне класса или нет.
Ответ №2:
Похоже на проблему импорта политик из объекта class A
и применения их к объектам class B
, с той лишь разницей, что все это делается во время выполнения (в отличие от типичного проектирования на основе политик). Возникает вопрос, являются ли эти политики собственностью class A
или мы можем изолировать их и передавать по мере необходимости? Это должно упростить определение.
Ответ №3:
Решение, основанное на шаблонах.
#include <iostream>
#include <string>
#include <vector>
// Wraps up a vector of type T and exposes a
// simple interface to access it.
template <class T>
class A
{
public:
A(const std::vector<T>amp; v) : priv_vec(v) { }
virtual size_t count() const { return priv_vec.size(); }
virtual T operator[](size_t index) const { return priv_vec[index]; }
private:
std::vector<T> priv_vec;
};
// A sample functor with state.
// The state is the size and current index of the vector object.
class Functor
{
public:
Functor() : _size(0), _index(0) { }
// Prints the element at the current index.
// If the index exceeds size, it is reset to 0.
template <class T>
void operator()(const A<T>amp; b)
{
if (_size == 0) _size = b.count();
if (_index >= _size) _index = 0;
std::cout << b[_index ] << 'n';
}
private:
size_t _size;
size_t _index;
};
int
main()
{
// Some tests.
std::vector<int> int_vec;
int_vec.push_back(1);
int_vec.push_back(2);
int_vec.push_back(3);
A<int> a(int_vec);
std::vector<std::string> str_vec;
str_vec.push_back("aaaa");
str_vec.push_back("bbbb");
str_vec.push_back("cccc");
A<std::string> b(str_vec);
Functor f;
f(a); // 1
f(b); // bbbb
f(a); // 3
f(a); // 1
f(a); // 2
f(b); // cccc
return 0;
}
Комментарии:
1. мои классы A и B недостаточно похожи, чтобы создать из них универсальный класс — но это можно обойти — проблема здесь в следующем: если я хочу, чтобы мой функтор мог изменять заданные векторы (например: сортировать их), то такой интерфейс предоставил бы личные данные (или я здесь что-то упускаю?)
2. @Dane Пожалуйста, имейте в виду, что мой код просто демонстрирует идею. Если вы позволяете своему функтору сортировать вектор на месте, то это действительно плохой дизайн. Возможно, вы все еще захотите сделать это ради повышения производительности, но в идеале функтор должен извлекать элементы из вектора (используя operator[] в классе A) и создавать новый, отсортированный вектор. (своего рода функциональное программирование!)