Nano Hash - криптовалюты, майнинг, программирование

Шаблонные функторы C++

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


Ответы:


1

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

Они работают, создавая собственный класс или структуру, которая перегружает оператор вызова функции (называемый 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

Это избавит вас от необходимости печатать. ;)

06.10.2009

2

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

Это все, что нужно шаблону. Вещь, для которой 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
06.10.2009
  • +1, лучший ответ, потому что вы действительно показываете пример функтора template. 07.10.2009
  • Что не так с этим синтаксисом void ( &(int) )? По крайней мере, это использование не C++. Это C++/CLI? 07.10.2009
  • Что случилось с этим синтаксисом void ( &(int) )? Да, это ошибка адреса функции, принимающей int и возвращающей void. 07.10.2009
  • @tpdi, ах, теперь это имеет смысл. Я на самом деле перепутал переменные br и bf и подумал в духе wtf, зачем передавать Foo в указатель на функцию :) Теперь я вижу, как вы передаете только &resize в br - хотя вы хотите использовать void(*)(int) (void(int) тоже будет работать, но void(&)(int) выиграл 't - см. заголовок stackoverflow.com/questions/1516958/ по этой причине) :) 07.10.2009
  • В любом случае, кроме этой незначительной проблемы, мне очень нравится ваше объяснение. Хорошая работа, товарищ 07.10.2009
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..