С ++ шаблонные функторы - PullRequest
       31

С ++ шаблонные функторы

1 голос
/ 07 октября 2009

Мне было интересно, может ли кто-нибудь помочь мне с функторами. Я действительно не понимаю, что такое функторы и как они работают. Я пытался найти их в Google, но до сих пор не понимаю. как работают функторы и как они работают с шаблонами

Ответы [ 2 ]

6 голосов
/ 07 октября 2009

Функтор - это, по сути, «функциональный объект». Это отдельная функция, которую вы завернули в класс или структуру, и которую вы можете передать другим функциям.

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

Предположим, у вас есть следующее:

std::vector<int> counts;

Теперь вы хотите увеличить все значения, содержащиеся в этом векторе. Вы можете перебрать их вручную, чтобы увеличить их, или использовать функтор. Подходящий функтор в этом случае будет выглядеть так:

struct IncrementFunctor
{
    int operator() (int i)
    {
        return i + 1;
    }
}

IncrementFunctor теперь является функтором, который берет любое целое число и увеличивает его. Чтобы применить его к счетчикам, вы можете использовать функцию std :: transform, которая принимает в качестве аргумента функтор.

std::transform(
    counts.begin(),       // the start of the input range
    counts.end(),         // the end of the input range
    counts.begin(),       // the place where transform should place new values. 
                          // in this case, we put it right back into the original list.
    IncrementFunctor());  // an instance of your functor

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

Теперь на шаблоны. Тип функтора в std :: transform является аргументом шаблона. Это потому, что std :: transform не знает (или не заботится!) О типе вашего функтора. Все, что его волнует, - это то, что у него есть определенный оператор fit (), для которого он может сделать что-то вроде

newValue = functor(oldValue);

Компилятор довольно умён в отношении шаблонов и часто может сам определить, каковы аргументы шаблона. В этом случае компилятор автоматически понимает, что вы передаете параметр типа IncrementFunctor, который определен как тип шаблона в std :: transform. Он делает то же самое для списка, поэтому компилятор автоматически распознает, что фактический вызов будет выглядеть следующим образом:

std::transform<std::vector<int>::iterator, // type of the input iterator
               std::vector<int>::iterator, // type of the output iterator
               IncrementFunctor>(          // type of your functor
    counts.begin(),       // the start of the input range
    counts.end(),         // the end of the input range
    counts.begin(),       // the place where transform should place new values. 
                          // in this case, we put it right back into the original list.
    IncrementFunctor());  // an instance of your functor

Это сэкономит вам немного времени при наборе текста. ;)

5 голосов
/ 07 октября 2009

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

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

За пределами шаблонного метапрограммирования мы обычно не говорим, что свободная функция является функтором, и резервируем это имя для экземпляра класса, который переопределяет operator()():

struct Foo {
 public:
   void operator()( int i ) { // do something }
   void operator()( int i, char x ) { // do something else }
}

В C ++ шаблоны компилируются, поэтому, пока синтаксис имеет смысл , компилятор с радостью использует функцию или функтор:

template<typename T> class Bar {
  private int j ;
  public:
    Bar( int i ) : j(i) {}
    void doIt(T t) {
     t( j ) ;
  }
}

Foo f;
extern void resize( int i ) ; // in some header

Bar<Foo> bf( 5 ) ;
// a Bar that is templated on Foo
Bar< void (&)(int)   > br( 5 ) ; 
// a Bar that is templated on a function taking int and returning void

br.doit( &resize ) ; // call resize with br.j
bf.doit( f ) ; // call Foo::operator()(int) on Foo f with bf.j
...