Мне было интересно, может ли кто-нибудь помочь мне с функторами. Я действительно не понимаю, что такое функторы и как они работают. Я пытался погуглить, но до сих пор не понимаю. как работают функторы и как они работают с шаблонами
Шаблонные функторы C++
Ответы:
Функтор - это в основном «функциональный объект». Это единственная функция, которую вы обернули в класс или структуру и которую вы можете передать другим функциям.
Они работают, создавая собственный класс или структуру, которая перегружает оператор вызова функции (называемый 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 не знает (или не заботится!) о типе вашего функтора. Все, о чем он заботится, это то, что у него определен подходящий оператор(), для которого он может сделать что-то вроде
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
Это избавит вас от необходимости печатать. ;)
Функтор — это то, что можно вызывать/вызывать с помощью оператора вызова функции, синтаксически добавляя ()
с необязательным списком аргументов в круглых скобках.
Это все, что нужно шаблону. Вещь, для которой this вызывается, что касается шаблона, - это все, что допускает этот синтаксис, или, другими словами, либо свободная функция, либо экземпляр класса, который переопределяет 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
void ( &(int) )
? По крайней мере, это использование не C++. Это C++/CLI? 07.10.2009 br
и bf
и подумал в духе wtf, зачем передавать Foo
в указатель на функцию :) Теперь я вижу, как вы передаете только &resize
в br
- хотя вы хотите использовать void(*)(int)
(void(int)
тоже будет работать, но void(&)(int)
выиграл 't - см. заголовок stackoverflow.com/questions/1516958/ по этой причине) :) 07.10.2009