Плюсы и минусы реализации функциональности как функтора
У меня есть дискуссия с коллегой об API простого класса, который имеет только один общедоступный метод. Я изначально пошел за:
class CalculateSomething
{
public:
void operator()(const SomeObject &obj) const;
private:
// ...
}
Однако мой коллега выступает против использования оператора() и хочет просто называть метод "вычислять" для ясности.
Хотя я не считаю этот аргумент убедительным, это заставило меня задуматься о плюсах и минусах.
Преимущества operator()
- Класс является худым и имеет одну четко определенную цель. После создания экземпляра он действует как свободная функция.
- Это функтор и может быть легко использован как таковой (например, алгоритмы STL).
- При использовании алгоритмов с диапазоном объект может передаваться напрямую, а не через указатель функции, который позволяет компилятору встраивать код. Хотя это и не гарантировано, передача его через указатель функции полностью запрещает эту возможность.
Недостатки operator()
- Менее понятно, что делает этот метод, не глядя на имя класса. (Я лично не согласен с тем, что класс имеет только один метод, и поэтому его значение ясно из названия класса)
- Ожидается, что большинство функторов в STL будут неактивными. Это, я думаю, является основной причиной, которая удерживает меня...
Я был удивлен, увидев, что мой поиск по этому поводу не вызвал многого, поскольку я бы предположил, что это довольно распространенный сценарий (один класс, одна ответственность). Таким образом, мне действительно интересно узнать, что думают другие люди.
Ответы
Ответ 1
Если lambdas действительно не вариант, ваш выбор должен зависеть от объема работы, которую этот объект берет на себя... и вызывать свои условные обозначения или стили кодирования. Вы можете решить быть явным (см. Werolik), это хорошо, если метод относительно незнакомы и требует состояний, но
Давайте рассмотрим простые примеры использования из стандартной библиотеки...
- std:: hash: Это объект функции, который выполняет одно задание, и делает это якобы хорошо, не оспаривает этого
- так много других... включая std:: less и его ассортимент
Одна вещь, которую вы видите вместе со всеми, заключается в том, что они глаголы... В моей перспективе, если ваш класс точно так же, как вы опубликовали фрагмент, CalculateSomething
означает действие для меня, поэтому я всегда могу указать его как CalculateSomething()(my_object...)
.
И, как вы процитировали, он очень удобен в использовании самого алгоритма STL и многих других библиотек С++. Если вы примете метод коллеги, вам может потребоваться использовать std:: binds и lambdas, потому что вы хотите "адаптировать" интерфейс.
Пример:
class CalculateSomething
{
public:
void operator()(const SomeObject &obj) const;
private:
// ...
}
class CalculateNothing
{
public:
void calculate(const SomeObject &obj) const;
private:
// ...
}
Пример использования:
std::for_each(container.begin(), container.end(), CalculateSomething());
против
std::for_each(container.begin(), container.end(), [ c = CalculateNothing()](auto x) { c.calculate(x); });
Я думаю, что предпочитаю первое.
Ответ 2
Ну, вот и мои 5 центов.
Прежде всего, если это возможно, я просто использую лямбду или бесплатную функцию. Вы уверены, что вам нужен класс для ваших нужд?
В любом случае, предполагая, что класс требуется.
- Мне не нравится использовать operator(). Возможно, с дополнительными комментариями.
- Возможно, было бы хорошо переименовать его в '
SomethingCalculator
' - разве это не объект?
- Чем вы можете добавить метод, например '
DoWork
', 'Calculate
' или что-то еще.
Это сделает его более явным.
UPD. Все вышеизложенное может быть по-прежнему аргументировано, но я действительно верю, что добавление достаточной документации внутри кода сделает реальную разницу независимо от имени метода.