Используя алгоритмы STL, лучше ли передавать указатель на функцию или функтор?

Какой из этих двух методов лучше и почему?

Способ 1:

void fun(int i) {
  //do stuff
}

...
for_each(a.begin(), a.end(), fun);

Способ 2:

class functor {
public:
  void operator()(int i);
};

...
for_each(a.begin(), a.end(), functor());

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

Спасибо большое!

Ответ 1

Функторы могут (и будут) тривиально инкрустированы - это не делается для регулярных указателей функций.

Таким образом, функторы имеют реальное преимущество в производительности, которое может быть огромным в жестких циклах. Кроме того, функторы, как правило, легче скомпилируются и, в частности, играют лучше с STL: std::bind x не работает, например, с указателями функций.

Я ненавижу, как они загромождают код, но все преимущества, я бы предпочел бы их над указателями функций в любое время.

Ответ 2

Чтобы прояснить неверное представление о том, что компилятор может встроить, достаточно хороший компилятор может встроить указатели на функции. Он может просто упростить функции объектов, поскольку имеется более статическая информация. Например, указатель на функцию, которая не принимает параметров и возвращает bool, имеет тип bool (*)(), тогда как функтор имеет явный тип, а именно, функтор, а экземпляр шаблона может статически вызвать оператор-функтор, а чем необходимость вызова указателя функции.

На практике, однако, это главным образом вопрос предоставления компилятору достаточной информации для эффективной оптимизации.

Например, Visual С++ 2008, учитывая следующий код с полной оптимизацией:

#include "stdafx.h"
#include <algorithm>

const char print_me[]= "hello!";

class print_functor
{
public:
    void operator()(char c)
    {
        printf("%c", c);
    }
};

void print_function(char c)
{
    printf("%c", c);
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::for_each(print_me, print_me + sizeof(print_me)/sizeof(print_me[0]), print_functor());
    printf("\n");

    std::for_each(print_me, print_me + sizeof(print_me)/sizeof(print_me[0]), print_function);

    return 0;
}

заключает в себе как std::for_each вызовы полностью. Кстати, на ПК первый for_each имеет ненужный lea ecx, [ecx].

Ответ 3

Одним большим преимуществом функционального объекта над указателем функции является то, что вы можете легче связать некоторые аргументы при построении объектов объектов.

Примером функтора, который может это сделать, будет

  class multiplyBy
  {
  private:
      int m_whatToMultiplyBy;
  public:
      multiplyBy(int whatToMultiplyBy) : 
          m_whatToMultiplyBy(whatToMultiplyBy)
      {
      }

      void operator()(int& i)
      {
          i = m_whatToMultiplyBy * i;
      }
  }


  ...

  // double the array
  for_each(a.begin(), a.end(), multiplyBy(2));

Эта "привязка" аргументов может быть выполнена довольно хорошо с помощью boost:: bind и boost:: function, если вам доступно повышение.

Ответ 4

Мое мнение - # 1 лучше, потому что оно проще.

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

Ответ 5

Функтор может быть более легким встраиванием, поэтому это может быть фактором, который следует учитывать при важности производительности.

Ответ 6

# 1 проще объявить функцию в то время как # 2 функтор больше похож на вызов функции.

(Иногда вам нужно отчаиваться синтаксисом С++)