Шаг 248.
Библиотека STL. Объекты функций STL. Написание пользовательских объектов функций для функциональных адаптеров

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

    Вы можете написать собственный объект функции, однако, чтобы он работал с функциональными адаптерами, в этом объекте должны быть определены типы для аргументов и результата. Для этого в стандартную библиотеку C++ были включены специальные структуры:

template <class Arg, class Result>
struct unary_function {
    typedef Arg    argument_type;
    typedef Result result_type;
};

template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1   first_argument_type;
    typedef Arg2   second_argument_type;
    typedef Result result_type;
};

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

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

#include <functional> 
#include <cmath>

template <class T1, class T2>
struct fopow : public std::binary_function<Tl, T2, T1>
{
  T1 operator() (T1 base, T2 exp) const {
    return std::pow(base,exp); 
  }
};

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

    Следующая программа показывает, как работать с пользовательским объектом функции fopow. В частности, продемонстрировано использование fopow с адаптерами bind1st и bind2nd:

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

#include <vcl.h>
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <functional>
#include <cmath>

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

#pragma hdrstop

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

#pragma argsused
using namespace std;


template <class T1, class T2>
struct fopow : public std::binary_function <T1, T2, T1>
{
  T1 operator() (T1 base, T2 exp) const {
    return std::pow(base,exp); 
  }
};


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()
{
  vector<int> coll;

  // Вставка элементов со значениями от 1 до 9
  for (int i=1; i<=9; ++i) {
      coll.push_back(i);
  }
  PRINT_ELEMENTS(coll,"Исходный вектор:\n");
  
  // Вывод числа 3, возведенного в степень каждого элемента
  cout << ToRus("Вывод числа 3, возведенного в степень каждого элемента:\n");
  transform (coll.begin(), coll.end(),         // Источник
             ostream_iterator<int>(cout," "),  // Приемник
             bind1st(fopow<float,int>(),3));   // Операция
  cout << endl;

  // Вывод всех элементов, возведенных в степень 3
  cout << ToRus("Вывод всех элементов, возведенных в степень 3:\n");
  transform (coll.begin(), coll.end(),         // Источник
             ostream_iterator<int>(cout," "),  // Приемник
             bind2nd(fopow<float,int>(),3));   // Операция
  cout << endl;

  getch();
  return 0;
}

//---------------------------------------------------------------------------
Текст этого примера можно взять здесь.

    Программа выводит следующий результат:


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

    Обратите внимание: объект функции fopow реализован для типов float и int. Если использовать int как для основания, так и для показателя степени, вы сможете вызвать pow() с двумя аргументами типа int, но при этом будет нарушена переносимость кода, поскольку в соответствии со стандартом функция pow() перегружается более чем для одного, но не для всех базовых типов:

  transform (coll.begin(), coll.end(),
             ostream_iterator<int>(cout," "),
             bind1st(fopow<int,int>(),3));    //ОШИБКА: неоднозначность

    На следующем шаге мы рассмотрим дополнительные композиционные адаптеры.




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