На этом шаге мы рассмотрим особенности написания пользовательских объектов функций для функциональных адаптеров.
Вы можете написать собственный объект функции, однако, чтобы он работал с функциональными адаптерами, в этом объекте должны быть определены типы для аргументов и результата. Для этого в стандартную библиотеку 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)); //ОШИБКА: неоднозначность
На следующем шаге мы рассмотрим дополнительные композиционные адаптеры.