Класс-функтор, выполняющий работу в конструкторе
-
05-07-2019 - |
Вопрос
Я использую шаблоны C++ для передачи функторов стратегии, чтобы изменить поведение моей функции.Он работает нормально.Функтор, который я передаю, представляет собой класс без сохранения состояния, не имеющий хранилища, и он просто перегружает оператор () классическим функторным способом.
template <typename Operation> int foo(int a)
{
int b=Operation()(a);
/* use b here, etc */
}
Я делаю это часто, и это хорошо работает, и часто я создаю шаблоны с 6 или 7 переданными шаблонными функторами!
Однако меня беспокоит как элегантность кода, так и эффективность.Функтор не имеет состояния, поэтому я предполагаю, что конструктор Operation() бесплатен, а оценка функтора так же эффективна, как и встроенная функция, но, как и у всех программистов C++, у меня всегда есть некоторые мучительные сомнения.
Мой второй вопрос: могу ли я использовать альтернативный функторный подход.тот, который не переопределяет оператор (), но делает все в конструкторе как побочный эффект!Что-то вроде:
struct Operation {
Operation(int a, int &b) { b=a*a; }
};
template <typename Operation> int foo(int a)
{
int b;
Operation(a,b);
/* use b here, etc */
}
Я никогда не видел, чтобы кто-нибудь использовал конструктор в качестве «работы» функтора, но кажется, что он должен работать.Есть ли преимущество?Какой-нибудь недостаток?Мне нравится удаление странной двойной скобки «Operator()(a)» , но, скорее всего, это просто эстетика.
Решение
Какой-нибудь недостаток?
- Векторы не возвращают никакого полезного значения — их нельзя использовать в цепочках вызовов (например,фу(бар()).
- Они могут бросить.
- С точки зрения дизайна: векторы — это функции создания объектов, которые на самом деле не предназначены для использования в качестве рабочих лошадок.
Другие советы
Благодаря производительности, код, продемонстрированный с, был полностью оптимизирован как для VC, так и для GCC. Тем не менее, лучшей стратегией часто является использование функтора в качестве параметра, чтобы получить гораздо большую гибкость и идентичные характеристики производительности.
Я бы рекомендовал определить функтор, который работает с STL-контейнерами, т. е. они должны реализовывать operator (). (Следовать API языка, который вы используете, всегда хорошая идея.)
Это позволяет вашим алгоритмам быть очень общими (передавать функции, функторы, stl-bind, boost :: function, boost :: bind, boost :: lambda, ...), что обычно и требуется. р>
Таким образом, вам не нужно указывать тип функтора в качестве параметра шаблона, просто создайте экземпляр и передайте его:
my_algorithm(foo, bar, MyOperation())
Нет смысла реализовывать конструктор в другом классе.
Все, что вы делаете, это нарушаете инкапсуляцию и настраиваете свой класс на злоупотребления.
Конструктор должен инициализировать объект в хорошем состоянии, как определено классом. Вы позволяете другому объекту инициализировать ваш класс. Какие у вас гарантии, что этот шаблонный класс знает, как правильно инициализировать ваш класс? Пользователь вашего класса может предоставить любой объект, который может связываться с внутренним состоянием вашего объекта способами, не предназначенными.
Класс должен быть автономным и инициализироваться в хорошем состоянии. Похоже, что вы играете с шаблонами, просто чтобы посмотреть, на что они способны.