Шаг 117.
Библиотека STL.
Стандартные объекты функций

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

    Стандартная библиотека C++ содержит несколько объектов функций для выполнения базовых операций. Иногда эти объекты избавляют программиста от необходимости создавать собственные объекты функций. Типичным примером служит объект функции, используемый как критерий сортировки. По умолчанию сортировка с применением оператора < осуществляется с критерием less<>. Рассмотрим следующее объявление:

  set<int> coll;

    Это объявление расширяется до:

  set <int,less <int> > col;   // Сортировка элементов оператором <

    Так же просто обеспечивается сортировка элементов в обратном порядке:

  set<int,greater<int> > coll; // Сортировка элементов оператором >


   Замечание. Обратите внимание на пробел между символами >. Последовательность > воспринимается компилятором как оператор сдвига, что приводит к синтаксической ошибке.

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

  transform (coll.begin(), coll.end(),  // Источник
    coll.begin(),                       // Приемник
    negate<int>());                     // Операция

    Выражение negate<int>() создает объект функции стандартного шаблонного класса negate. Этот объект просто возвращает элемент типа int, для которого он был вызван, с противоположным знаком. Алгоритм transform() использует эту операцию для преобразования всех элементов первой коллекции во вторую коллекцию. Если источник и приемник совпадают (как в нашем случае), возвращаемые элементы с измененным знаком записываются на место исходных. Таким образом, приведенная выше команда меняет знак всех элементов коллекции.

    Возведение всех элементов коллекции в квадрат выполняется аналогично:

  transform (coll.begin(), coll.end(), // Первый источник
    coll.begin(),                      // Второй источник
    coll.begin(),                      // Приемник
    multiplies<int>());	               // Операция

    На этот раз другая форма алгоритма transform() объединяет элементы двух коллекций, применяя к ним заданную операцию, и записывает результат в третью коллекцию. В данном примере во всех трех случаях используется одна и та же коллекция, поэтому каждый элемент умножается сам на себя, а результат записывается в исходную позицию.

    Специальные функциональные адаптеры позволяют объединять стандартные объекты функций с другими значениями или определять особые ситуации. Пример:

//---------------------------------------------------------------------------

#include <vcl.h>
#include <iostream>
#include <set>
#include <deque>
#include <algorithm>
#include <conio.h> //необходимо для getch()

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
using namespace std;

std::string ToRus(const std::string &in)
{
  char *buff = new char [in.length()+1];
  CharToOem(in.c_str(),buff);
  std::string out(buff);
  delete [] buff;
  return out;
}

template <class T>
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="")
{
  typename T::const_iterator pos;
  std::cout << ToRus(optcstr);
  for (pos=coll.begin(); pos!=coll.end(); ++pos) {
    std::cout <<*pos <<' ';
  }
  std::cout << std::endl;
}

int main(int argc, char* argv[])
{
  set<int,greater<int> > coll1; 
  deque<int> coll2;

  // Вставка элементов со значениями от 1 до 9 
  for (int i=1; i<=9; ++i) { 
    coll1.insert(i);
  }

  PRINT_ELEMENTS(coll1,"Инициализация:\n");

  // Преобразование всех элементов coll2 умножением на 10
  transform (coll1.begin(),coll1.end(), // Источник
    back_inserter(coll2),               // Приемник
    bind2nd(multiplies<int>(),10));     // Операция
  PRINT_ELEMENTS(coll2,"После преобразования:\n");

  // Замена значения, равного 70, на 42
  replace_if (coll2.begin(),coll2.end(), // Интервал
    bind2nd(equal_to<int>(),70),         // Критерий замены
    42);                                 // Новое значение
  PRINT_ELEMENTS(coll2,"После замены:\n");

  // Удаление всех элементов со значениями, меньшими 50
  coll2.erase(remove_if(coll2.begin(),coll2.end(), // Интервал
    bind2nd(less<int>(),50)),                      // Критерий удаления
    coll2.end());
  PRINT_ELEMENTS(coll2,"После удаления:\n");

  cout << endl;

  getch();
  return 0;
}
//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Следующая команда умножает каждый элемент coll1 на 10 и вставляет его в coll2:

  transform (coll1.begin(),coll1.end(), // Источник
    back_inserter(coll2),	        // Приемник
    bind2nd(multiplies<int>(),10));     // Операция

    Функциональный адаптер bind2nd обеспечивает вызов multiplies<int> для каждого элемента исходной коллекции (первый аргумент) и множителя 10 (второй аргумент).

    При вызове bind2nd() происходит следующее: в четвертом аргументе алгоритм transform() рассчитывает получить операцию, которая вызывается с одним аргументом (элементом, с которым она выполняется). В нашем примере аргумент умножается на 10. Следовательно, мы должны объединить операцию с двумя аргументами и постоянную величину, которая всегда будет использонаться вместо второго аргумента; в результате будет получена нужная операция с одним аргументом. Именно это и делает адаптер bind2nd(). Он сохраняет операцию и второй аргумент в своих внутренних данных. Когда алгоритм вызывает адаптер bind2nd для конкретного элемента, вызывается операция с переданным элементом (первый аргумент) и внутренним значением (второй аргумент), а возвращается полученный результат.

    В аналогичном фрагменте выражение bind2nd(equal_to<int>(),70) определяет критерий отбора элементов, заменяемых значением 42:

  replace_if (coll2.begin(),coll2.end(), 
    bind2nd(equal_to<int>(),70), 
    42);

    Адаптер bind2nd вызывает бинарный предикат equal_to со вторым аргументом, равным 70. Тем самым определяется унарный предикат для элементов обрабатываемой коллекции.

    Последняя команда действует аналогично. Выражение bind2nd(less<int>(),50) определяет элементы, которые должны быть удалены из коллекции. Команда удаляет все элементы со значением меньше 50. Результат выполнения программы выглядит так:


Рис.1. Результат работы приложения

    Подобный подход к программированию приводит к функциональной композиции. Интересная подробность: все объекты функций обычно объявляются подставляемыми (inline). Таким образом, несмотря на абстрактную функциональную запись, мы получаем хорошую производительность.

    Существуют и другие разновидности объектов функций. Например, некоторые объекты функций позволяют вызвать определенную функцию класса для каждого элемента коллекции:

  for_each (coll.begin(),coll.end(),  // Интервал
    mem_fun_ref(&Person_save));       // Операция

    Объект функции mem_fun_ref вызывает заданную функцию класса для того элемента, для которого этот объект вызывается. В приведенном примере для каждого элемента коллекции coll вызывается функция save() класса Person. Разумеется, эта конструкция работает только в том случае, если элемент относится к типу Person или производному от него.

    Со следующего шага мы начнем рассматривать элементы контейнеров.




Предыдущий шаг Содержание Следующий шаг