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

Связыватели

Рассмотрим следующую ситуацию: имеется вектор целых чисел, необходимо удалить все числа меньше 20. Несложно написать код, решающий эту задачу, достаточно написать соответствующий предикат:

#include <iostream>

#include <algorithm>

#include <functional>

#include <vector>

#include <cstdlib>

using namespace std;

int gen_random()

{

return rand() % 50;

}

void print(int arg)

{

cout << arg << ',';

}

bool less_than_20(int arg)

{

return arg < 20;

}

int main (int, char**)

{

vector<int> vec(20);

generate(vec.begin(),vec.end(),gen_random);

for_each(vec.begin(),vec.end(),print);

cout << endl;

vec.erase(remove_if(vec.begin(),vec.end(),less_than_20),

vec.end());

for_each(vec.begin(),vec.end(),print);

cout << endl;

return 0;

}

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

#include <iostream>

#include <algorithm>

#include <functional>

#include <vector>

#include <cstdlib>

using namespace std;

int gen_random()

{

return rand() % 50;

}

void print(int arg)

{

cout << arg << ',';

}

class less_than

{

public:

less_than(int th): threshold(th) {}

bool operator() (int arg)

{

return arg < threshold;

}

private:

Int threshold;

};

int main (int, char**)

{

vector<int> vec(20);

generate(vec.begin(),vec.end(),gen_random);

for_each(vec.begin(),vec.end(),print);

cout << endl;

// Удалить элемента меньше 20

vec.erase(remove_if(vec.begin(),vec.end(),less_than(20)),

vec.end());

for_each(vec.begin(),vec.end(),print);

cout << endl;

// Удалить элемента меньше 40

vec.erase(remove_if(vec.begin(),vec.end(),less_than(40)),

vec.end());

for_each(vec.begin(),vec.end(),print);

cout << endl;

return 0;

}

Это решение значительно лучше предыдущего, но тоже не лишено недостатков. Так если понадобиться удалять элемента больше какого-то значения, или элементы которые не меньше заданного значения, придется разрабатывать новый класс функтора. Кроме того, вспомним, что стандартная библиотека содержит стандартные функторы для операций сравнения (например less), которые желательно было бы использовать, и которые не могут быть использован в своем обычном виде так как принимают два аргумента вместо одного.

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

// Связывает первый параметра функтора принимающего два параметра

// со значением

template<class BinFunction>

class binder1st : public unary_function<BinFunction::second_argument_type,

BinFunction::result_type>

{

public:

binder1st(const BinFunction& op,

const BinFunction::first_argument_type& y)

: bin_op(op), value(y) {}

result_type operator()(const BinFunction::second_argument_type& x) const

{

return (bin_op(value, x));

}

protected:

BinFunction bin_op;

BinFunction::first_argument_type value;

};

// Связывает второй параметра функтора принимающего два параметра

// со значением

template<class BinFunction>

class binder2nd: public unary_function<BinFunction::first_argument_type,

BinFunction::result_type>

{

public:

binder2nd(const BinFunction& op,

const BinFunction::second_argument_type& y)

: bin_op(op), value(y) {}

result_type operator()(const BinFunction::first_argument_type& x) const

{

return (bin_op(x, value));

}

protected:

BinFunction bin_op;

BinFunction::second_argument_type value;

};

Принцип работы связывателей достаточно прост. Конструктор связывателя принимает в качестве параметров функтор, параметр которого связывается, и связываемое значение, и сохраняет их во внутренних полях. В момент вызова (с одним аргументом), связыватель переадресует вызов функтору, который он сохранил в конструкторе, и передает ему в качестве аргументов собственный аргумент (не связанный аргумент) и значение, которое он сохранил в конструкторе (связанный аргумент).

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

template<class BinFunction, class T>

inline binder1st<BinFunction> bind1st(const BinFunction & x,

const T& y)

{

return (binder1st<BinFunction>(x,y));

}

template<class BinFunction, class T>

inline binder2nd<BinFunction> bind2nd(const BinFunction& x,

const T& y)

{

return (binder2nd<BinFunction>(x,y));

}

Функции bind1stиbind2nd принимают такие же параметра как и конструкторы связывателей, позволяя явно не указывать параметры шаблона, а выводить их автоматически на основе параметров функции.

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

// Явное определение типа функтора, а также параметров шаблона

vec.erase(remove_if(vec.begin(),vec.end(),

binder2nd<less<int> >(less<int>(),20)),

vec.end());

// Более удобное использование функции-помощника

vec.erase(remove_if(vec.begin(),vec.end(),bind2nd(less<int>(),20)),

vec.end());

Необходимо использовать less<int>(), а неless<int>, так какless<int> является именем типа и не может быть параметром функции.

1Помимо отрицателей эти имена типов используются привязывателями, который рассмотрены далее.

2Приведенный ниже пример не компилируется вVC++ 6.0, пример протестирован наgcc3.4.4

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