Существует ли способ построить в классе группу "одинаковых" методов, но использующих каждый одно уникальное свойство/параметр, при помощи шаблона (template) или других техник, чтобы не писать определения для каждого "типового" метода?
Поясню примером - ниже код (используемый в блоке шаблона проектирования "наблюдатель"), который хотелось-бы упростить/сократить при помощи использования "template" (или других методов) чтобы уменьшить вероятность возможных ошибок при увеличении количества и функционала наблюдателей:
class MyService {
viod add_f0_observer(MyType& ref);
viod add_f1_observer(MyType& ref);
viod add_f2_observer(MyType& ref);
void event_caller();
MyType* f0_observer;
MyType* f1_observer;
MyType* f2_observer;
}
viod MyService::add_f0_observer(MyType& ref) {
f0_observer = &ref;
// ...
}
viod MyService::add_f1_observer(MyType& ref) {
f1_observer = &ref;
// ...
}
viod MyService::add_f2_observer(MyType& ref) {
f2_observer = &ref;
// ...
}
В вашем конкретном примере общий код и так прост до предела, поэтому еще больше "упростить" его не получится. Но если предположить, что в реальном коде общий код будет существенно более объемным, отличаясь в трех своих вариантом только использованием поля f0_observer
, f1_observer
или f2_observer
, то возможность упрощения есть.
Эту задачу можно решить без шаблонов, объявив "универсальную" функцию с обычным параметром типа указатель-на-член-класса
class MyService
{
void add_observer(MyType *MyService::*field, MyType& ref)
{
// ...
this->*field = &ref;
// ...
}
void add_f0_observer(MyType& ref)
{ add_observer(&MyService::f0_observer, ref); }
void add_f1_observer(MyType& ref)
{ add_observer(&MyService::f1_observer, ref); }
void add_f2_observer(MyType& ref)
{ add_observer(&MyService::f2_observer, ref); }
MyType* f0_observer;
MyType* f1_observer;
MyType* f2_observer;
};
Понятно, что в данном примере и указатель-на-член-класса тут не обязателен: можно было бы в качестве первого параметра использовать обычную ссылку или указатель на MyType
. Но я специально использовал именно указатель-на-член-класса, ибо он в таком случае будет являться константой времени компиляции.
Если учесть, что такой указатель является константой времени компиляции, можно превратить его из обычного параметра в шаблонный параметр
class MyService
{
template <MyType *MyService::*field> void add_observer(MyType& ref)
{
// ...
this->*field = &ref;
// ...
}
void add_f0_observer(MyType& ref)
{ add_observer<&MyService::f0_observer>(ref); }
void add_f1_observer(MyType& ref)
{ add_observer<&MyService::f1_observer>(ref); }
void add_f2_observer(MyType& ref)
{ add_observer<&MyService::f2_observer>(ref); }
MyType* f0_observer;
MyType* f1_observer;
MyType* f2_observer;
};
Стоит ли это делать - вопрос отдельный.
Ту же самую идею можно реализовать не только через голые указатели, но и через более высокоуровневые средства языка: std::bind
или лямбды.
Я может и не очень понел ваши намерения, поскольку логично в классе просто хранить контейнер и каждый раз в нем просто добавлять элемент. Но, насколько я понел что вы хотите сделать, напишу пример:
template<size_t Tab, class Seq = std::vector< MyType*>>
class MyService {
//хранит контейнер нулевых указателей
Seq observers = Seq(Tab, nullptr);
//...
public:
template<size_t k>
void add_observer(MyType& ref)
{
//каким нибудь образом не позволить
// выход за пределы контейнера
if constexpr (k >= Tab)
observers[Tab - 1] = &ref;
else
observers[k] = &ref;
}
//...
};
//и если вам нужен класс с тремя указательями
//то инстанцируйте этот класс один раз
template class MyService<3>; //пользуется вектором
//дальше можете использовать например так:
void add(MyService<3> &s, std::array<MyType, 3> arr)
{
/*инстанцируем 3 функции_члена, каждая из которых
будет работать с соответствующим указателем*/
s.add_observer<0>(arr[0]);
s.add_observer<1>(arr[1]);
s.add_observer<2>(arr[2]); // или
s.add_observer<5>(arr[2]);
//...
}
Есть вариант с вынесением общей функциональности в шаблонную функцию, а конкретных реализаций - в перегруженную. Например:
class MyService{
public:
struct f0_observer{ static constexpr int id = 0;};
struct f1_observer{ static constexpr int id = 1;};
struct f2_observer{ static constexpr int id = 2;};
template<class O> auto add_observer(O type)->decltype(process(type)){
observers[type.id] = &ref;
/// Вместо type.id можно реализовать шаблонный список типов, позволяющий извлечь id по типу
process(type);
}
private:
void process(f0_observer){ /*...*/}
void process(f1_observer){ /*...*/}
void process(f2_observer){ /*...*/}
std::array<MyType*> observers
}
void foo(){
MyService s;
MyType t;
s.add_observer(MyService::f1_observer{}, &t);
/*...*/
}
Для данного конкретного случая это явно не оправдано.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Возник такой вопрос: как реализовать решение следующей задачи на C++? Вводятся число N - количество строкВ каждой из N строк через пробел вводятся...
Делаю приложение, которое запускается на Windows 7/10 от пользователя с правами администратораПри этом запускается оно в обычном режиме (с ограниченными...
хочу хранить в векторе некоторые структуры,(много)Интересует вопрос почему такой код работает быстрее