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

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

Метод 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());

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

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

Ответы [ 6 ]

19 голосов
/ 22 июня 2009

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

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

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

11 голосов
/ 22 июня 2009

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

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

Например, Visual C ++ 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].

7 голосов
/ 22 июня 2009

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

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

  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 , если вам доступно повышение.

6 голосов
/ 22 июня 2009

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

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

1 голос
/ 22 июня 2009

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

(Иногда приходится отчаиваться с синтаксисом c ++)

1 голос
/ 22 июня 2009

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

...