Плюсы и минусы реализации функциональности как функтора

Я обсуждаю с коллегой API простого класса, который имеет только один публичный метод. Я изначально пошел на:

class CalculateSomething
{
public:
void operator()(const SomeObject &obj) const;

private:
// ...
}

Тем не менее, мой коллега выступает против использования operator () и желает просто назвать метод «вычисления» для ясности.
Хотя я не считаю этот аргумент убедительным, он заставил меня задуматься о плюсах и минусах.

Преимущества оператора ()

  • Класс скуден и имеет единственную четко определенную цель. После создания он в основном действует как свободная функция.
  • Это функтор, и его можно легко использовать как таковой (например, алгоритмы STL).
  • При использовании его с алгоритмами диапазона объект может передаваться напрямую, а не через указатель функции, что позволяет компилятору встроить код. Хотя это и не гарантировано, его передача через указатель на функцию полностью запрещает эту возможность.

Недостатки оператора ()

  • Менее понятно, что делает метод, не глядя на имя класса. (Я лично не согласен, поскольку у класса есть только один метод, и поэтому его значение ясно из названия класса)
  • Ожидается, что большинство функторов в STL не будут иметь состояния. Это, я думаю, главная причина, которая сдерживает меня …

Я был удивлен, увидев, что мой поиск по этому вопросу не принес много пользы, так как я предположил, что это довольно распространенный сценарий (один класс, одна ответственность). Таким образом, мне действительно интересно услышать, что другие люди думают об этом.

12

Решение

Если лямбды действительно не вариант, ваш выбор должен зависеть от объема работ, выполняемых объектом … и от ваших соглашений или стилей кодирования. Вы можете решить быть явным (см. Werolikответ), это хорошо, если метод относительно незнаком и требует состояний, но

Давайте возьмем простые варианты использования из стандартной библиотеки …

  • станд :: хэш: Это функциональный объект, который выполняет одну работу, и, якобы, делает это хорошо, никаких споров по этому поводу
  • еще много всего… в том числе станд :: меньше, и его ассортимент

Общее, что вы видите во всем этом, это то, что они глаголы… С моей точки зрения, если ваш класс в точности соответствует фрагменту, который вы опубликовали, CalculateSomething означает действие для меня, поэтому я всегда могу создать его экземпляр как CalculateSomething()(my_object...),

И, как вы указали, это очень удобно при использовании самого алгоритма STL и многих других библиотек C ++. Если вы используете метод вашего коллеги, вам, возможно, придется прибегнуть к использованию 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); });

Я думаю, что предпочитаю первое.

3

Другие решения

Ну, вот и мои 5 центов.

Прежде всего, если возможно, я бы просто использовал лямбду или свободную функцию. Вы уверены, что вам нужен класс для ваших нужд?

Во всяком случае, предполагая, что класс необходим.

  1. Мне не очень нравится использовать operator (). Возможно с дополнительными комментариями.
  2. Вероятно, было бы хорошо переименовать его вSomethingCalculator«- разве это не объект?
  3. Чем вы можете добавить метод, что-то вродеDoWork‘,’Calculateили что угодно.

Это сделает это намного более явным.

UPD. Все вышеизложенное можно до сих пор аргументировать, но я искренне верю в то, что добавление достаточного количества документации в коде будет иметь реальное значение, независимо от имени метода.

1

По вопросам рекламы [email protected]