Когда функторы класса без состояния полезны вместо функции стиля ac? - PullRequest
6 голосов
/ 15 февраля 2012

Я нашел несколько хороших примеров функторов в SO, таких как this one, и все убедительные примеры, кажется, используют состояние в классе, который определяет operator().

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

Я знаю, std::sort позволяет вам выбирать между operator() классами и функциями, но я всегда просто использовал функции из-за приведенной выше логики.

По каким причинам класс может быть предпочтительным?

Вот пример (перефразированный):

class Point2D {
   //.. accessors, constructors
   int x,y;
};
class HorizComp {
public:
   bool operator()(const Point2D& p, const Point2D& q) const
   { return p.getX() < q.getX(); }
};

class VertComp {
public:
   bool operator()(const Point2D& p, const Point2D& q) const
   { return p.getY() < q.getY(); }
};

template <typename E, typename C>
void printSmaller(const E& p, const E& q, const C& isLess) {
   cout << (isLess(p, q) ? p : q) << endl; // print the smaller of p and q
}
//...
// usage in some function:
Point2D p(1.2, 3.2), q(1.5, 9.2);
HorizComp horizComp;
VertComp vorizComp;
printSmaller(p, q, horizComp);
printSmaller(p, q, vorizComp);

Ответы [ 5 ]

8 голосов
/ 15 февраля 2012

Типичная причина в том, что когда вы делаете это:

bool less_than(const Point&, const Point&);
// ...
std::sort(..., &less_than);

Аргумент шаблона для предиката следующий:

bool(const Point&,const Point&)

Поскольку функция сортировки получает указатель на функцию,компилятору сложнее встроить использование предикатов в std::sort().Это происходит потому, что у вас может быть другая функция

bool greater_than(const Point&, const Point&);

, которая имеет точно такой же тип, а это означает, что выражение std::sort() будет разделено между двумя предикатами.(помните, что я сказал, что это делает вставку более сложной , а не невозможной).

Напротив, когда вы делаете это:

struct less_than {
    bool operator()(const Point&, const Point&) const;
};
// ...
std::sort(..., less_than());


struct greater_than {
    bool operator()(const Point&, const Point&) const;
};
// ...
std::sort(..., greater_than());

Компилятор генерирует уникальныйсоздание шаблона для std::sort() для каждого предиката, что упрощает включение определения предиката.

6 голосов
/ 15 февраля 2012

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

В C ++ 11 другая причина использовать класс для удобства - вы можете использовать лямбда-выражение для определения класса.

0 голосов
/ 24 апреля 2018

В библиотеках шаблонов, где состояние аргумента функтора неизвестно во время определения, аргумент типа класса обеспечивает более общий интерфейс, чем указатели на функции без экземпляра. STL является отличным примером, когда полные или не имеющие состояния пердикаты и функторы можно использовать в качестве параметров для классов и функций. Если программирование шаблона не является частью плана, указатель на функцию превосходит класс функторов без сохранения состояния; один экземпляр функции может принимать все указатели функций с определенной сигнатурой, а размер кода минимизируется. Но в случае минимальной вероятности расширения библиотеки в будущем, функтор делает его более универсальным.

0 голосов
/ 15 февраля 2012

Другая причина в том, что иногда одной функции сравнения недостаточно.Допустим, у нас есть вектор указателей:

struct X { string name; };
vector<shared_ptr<X>> v;

Теперь, если мы хотим отсортировать вектор по name, мы должны определить наш собственный предикат для функции sort:

struct Cmp1
{
    bool operator()(const shared_ptr<X>& left, const shared_ptr<X>& right) const
    { return left->name < right->name; }
};

Это круто, но что нам делать, когда нам нужно найти объекты с определенным именем?Для работы с equal_range предикат должен иметь две разные функции сравнения:

struct Cmp2
{
    bool operator()(const shared_ptr<X>& left, const string& right) const
    { return left->name < right; }

    bool operator()(const string& left, const shared_ptr<X>& right) const
    { return left < right->name; }
};

Это позволяет нам вызывать equal_range с объектом string name:

equal_range(v.begin(), v.end(), name, Cmp2())
0 голосов
/ 15 февраля 2012

Другие отметили способность компилятора встроить функтор.Еще одно возможное преимущество объектов-функторов по сравнению с указателями на функции - это гибкость.Функтор может быть шаблоном, может быть производным классом, возможно, он имеет конфигурацию во время выполнения (даже если в момент времени вызывается оператор () и т. Д.

...