Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STL5 / lab7-functors / lab7-functors.doc
Скачиваний:
18
Добавлен:
10.04.2015
Размер:
87.04 Кб
Скачать

Функторы совместимые с stl

Функторы широко используются вместе со стандартными алгоритмами STL, однако чтобы функтор был совместим сSTL1, необходимо, чтобы он удовлетворял некоторым требованиям. Совместимый сSTLфунктор должен определять следующие имена типов:

  • result_type– тип возвращаемого значения.

  • argument_type– тип аргумента (в случае функтора принимающего один аргумент).

  • first_argument_type– тип первого аргумента (в случае функтора принимающего два аргумента).

  • second_argument_type– тип второго аргумента (в случае функтора принимающего два аргумента).

STLсодержит вспомогательные классы, которые могут быть использованы в качестве базовых в случае создания совместимых сSTLфункторов:

template<class Arg, class Res>

struct unary_function

{

typedef Arg argument_type;

typedef Res result_type;

};

template<class Arg1, class Arg2, class Res>

struct binary_function

{

typedef Arg1 first_argument_type;

typedef Arg2 second_argument_type;

typedef Res result_type;

};

unary_functionдолжен быть использован в случае создания совместимого сSTLфунктора принимающего один аргумент,binary_functionв случае функтора принимающего два аргумента. Вспомогательные базовые классы должны быть использованы следующим образом:

#include <iostream>

#include <functional>

#include <algorithm>

#include <vector>

using namespace std;

template <class T>

void print_to_cout(const T& arg)

{

cout << arg << ',' ;

}

class is_even_predicate: public unary_function<int, bool>

{

public:

bool operator() (int arg)

{

return (arg % 2) == 0;

}

};

int main (int argc, char** argv)

{

vector<int> v1;

int i;

for (i = 0; i < 10; i++)

v1.push_back(i);

for_each(v1.begin(),v1.end(),print_to_cout<int>);

cout << endl;

v1.erase(remove_if(v1.begin(),v1.end(),is_even_predicate()),v1.end());

for_each(v1.begin(),v1.end(),print_to_cout<int>);

cout << endl;

return 0;

}

В случае двухаргументного функтора, в качестве базового класса должен использоваться binary_function

class add: public binary_function<int, int, int>

{

public:

int operator() (int arg1, int arg1)

{

return arg1 + arg2;

}

};

Все стандартные функторы STLявляются наследникамиunary_functionиbinary_function.

Определение базовых классов unary_functionиbinary_function, а также всех стандартных функторов находится в заголовочном файле <functional>

Передача функтора в функцию в качестве аргумента

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

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

#include <functional>

#include <algorithm>

#include <vector>

using namespace std;

class is_even_predicate: public unary_function<int, bool>

{

public:

bool operator() (int arg)

{

return (arg % 2) == 0;

}

};

int main (int argc, char** argv)

{

vector<int> v1;

… // заполнить вектор

v1.erase(remove_if<vector<int>::iterator, const is_even_predicate&>

(v1.begin(),v1.end(),is_even_predicate()),v1.end());

return 0;

}

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

Исходя из того, что функторы передаются по значению, при их создании необходимо придерживаться двух принципов (по большей части это советы, но их соблюдение стало общепризнанной нормой программирования STL):

  • Функциональный объект должен быть небольшим, в противном случае передача его в качестве параметра по значению будет очень затратной.

  • Функтор не должен быть полиморфным. При передаче полиморфного объекта по значению происходит урезание объекта до базового класса.

Естественное, что не всегда функторы имеют небольшой размер, естественно также, что иногда необходимо обеспечить функтору полиморфное поведение. В этих случае может быть использован метод разделения функтора на две части: часть, которая передается алгоритмам по значению, и часть, которая содержит реальные данные функтора и обеспечивает полиморфное поведение.

Предположим, существует следующий полиморфный функтор, содержащий большой объем данных

template<class T>

class BigPolymorhicFunctor : public unary_function<T, void>

{

public:

virtual void operator() (const T& val); // реализует полиморфное

// поведение

private:

BigDataClass data; // Класс содержит большой объем данных

}

Преобразуем класс следующим образом:

template<class T>

class BigPolymorhicFunctor : public unary_function<T, void>

{

public:

. . .

void operator() (const T& val)

{

impl->operator()(val);

}

private:

BigPolymorphicFunctorImpl* impl; // Указатель на реальный объект-функтор

// содержащий данные и реализующий

// полиморфное поведение

}

template<class T>

class BigPolymorphicFunctorImpl

{

public:

. . .

virtual void operator() (const T& val); // Реализует полиморфное

// поведение

private:

BigDataClass data;

}

Подобная реализация функтора позволит избежать копирования больших объемов данных и потери полиморфного поведения при передаче функтора BigPolymorhicFunctor по значению, однако необходимо понимать, что такая реализации приведет к существованию нескольких объектов BigPolymorhicFunctor указывающих на один и тот же объект BigPolymorphicFunctorImpl со всеми вытекающими отсюда трудностями.

Соседние файлы в папке lab7-functors