Чтение онлайн

на главную

Жанры

Эффективное использование STL
Шрифт:

 bind2nd(WidgetNameCompare, w)); // WidgetNameCompare

Если бы классы функторов не определялись производными от

unary_function
или
binary_function
, ни один из этих примеров не компилировался бы, поскольку
not1
и
bind2nd
работают только с адаптируемыми объектами функций.

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

operator
, типы параметров и возвращаемого значения которой должны передаваться
unary_function
или
binary_function
учетом правил передачи ссылок и указателей, о которых говорилось ранее). Из этого следует одно важное обстоятельство: не поддавайтесь соблазну и не пытайтесь объединять функциональность
WidgetnNameCompare
и
PtrWidgetCompare
в одной структуре с двумя функциями
operator
. В этом случае функтор будет адаптируемым по отношению лишь к одной из двух форм вызова (той, что использовалась при передаче параметров
binary_function
), а пользы от такого решения будет немного — наполовину адаптируемый функтор ничуть не лучше неадаптируемого.

Иногда в классе функтора бывает разумно определить несколько форм вызова, тем самым отказавшись от адаптируемости (примеры таких ситуаций приведены в советах 7, 20, 23 и 25), но это скорее исключение, а не правило. Адаптируемость важна, и о ней следует помнить при разработке классов функторов.

Совет 41. Разберитесь, для чего нужны ptr_fun, mem_fun и mem_fun_ref

Загадочные функции

ptr_fun/mem_fun/mem_fun_ref
часто вызывают недоумение. В одних случаях их присутствие обязательно, в других они не нужны… но что же они все-таки делают? На первый взгляд кажется, что они бессмысленно загромождают имена функций. Их неудобно вводить и читать, они затрудняют понимание программы. Что это — очередные пережитки прошлого STL (другие примеры приводились в советах 10 и 18) или синтаксическая шутка, придуманная членами Комитета по стандартизации с извращенным чувством юмора?

Действительно, имена выглядят довольно странно, но функции

ptr_fun
,
mem_fun
и
mem_fun_ref
выполняют важные задачи. Если уж речь зашла о синтаксических странностях, надо сказать, что одна из важнейших задач этих функций связана с преодолением синтаксической непоследовательности C++.

В C++ существуют три варианта синтаксиса вызова функции

f
для объекта
x
:

f(x); // Синтаксис 1: f не является функцией класса

//(вызов внешней функции)

x.f; // Синтаксис 2: f является функцией класса, а х

// является объектом или ссылкой на объект

p->f; // Синтаксис 3: f является функцией класса,

// а р содержит указатель на х

Рассмотрим гипотетическую функцию, предназначенную для «проверки» объектов

Widget
:

void test(Widget& w); // Проверить объект w. Если объект не проходит

// проверку, он помечается как "плохой"

Допустим, у нас имеется контейнер объектов

Widget
:

vector<Widget> vw; // vw содержит объекты Widget

Для проверки всех объектов

Widget
в контейнере
vw
можно воспользоваться алгоритмом
for_each
:

for_each(vw.begin, vw.end, test); // Вариант 1 (нормально компилируется)

Но представьте, что

test
является функцией класса
Widget
, а не внешней функцией (то есть класс
Widget
сам обеспечивает проверку своих объектов):

class Widget {

public:

 …

 void test; // Выполнить самопроверку. Если проверка

 … //
завершается неудачей, объект помечается

}; // как "плохой"

В идеальном мире мы могли бы воспользоваться

for_each
для вызова функции
Widget::test
всех объектов вектора
vw
:

for_each(vw.begin, vw.end,

 &Widget::test); // Вариант 2 (не компилируется!)

Более того, если бы наш мир был действительно идеальным, алгоритм

for_each
мог бы использоваться и для вызова
Widget::test
в контейнере указателей
Widget*
:

list<Widget*> lpw; // Список lpw содержит указатели

// на объекты Widget

for_each(lpw.begin, lpw.end, // Вариант 3 (не компилируется!)

 &widget::test);

Но подумайте, что должно было бы происходить в этом идеальном мире. Внутри функции

for_each
в варианте 1 вызывается внешняя функция, поэтому должен использоваться синтаксис 1. Внутри вызова
for_each
в варианте 2 следовало бы использовать синтаксис 2, поскольку вызывается функция класса. А внутри функции
for_each
в варианте 3 пришлось бы использовать синтаксис 3, поскольку речь идет о функции класса и указателе на объект. Таким образом, нам понадобились бы триразных версии
for_each
— разве такой мир можно назвать идеальным?

В реальном мире существует только одна версия

for_each
. Нетрудно представить себе возможную ее реализацию:

template<typename InputIterator, typename Function>

Function for_each(InputIterator begin, InputIterator end, Function f) {

 while (begin != end) f(*begin++);

}

Жирный шрифт используется для выделения того, что при вызове

for_each
используется синтаксис 1. В STL существует всеобщее правило, согласно которому функции и объекты функций всегда вызываются в первой синтаксической форме (как внешние функции). Становится понятно, почему вариант 1 компилируется, а варианты 2 и 3 не компилируются — алгоритмы STL (в том числе и
for_each
) жестко закодированы на использование синтаксиса внешних функций, с которым совместим только вариант 1.

Теперь понятно, для чего нужны функции

mem_fun
и
mem_fun_ref
. Они обеспечивают возможность вызова функций классов (обычно вызываемых в синтаксисе 2 и 3) при помощи синтаксиса 1.

Принцип работы

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

template<typename R, typename C> // Объявление mem_fun для неконстантных

 mem_fun_t<R, C> // функций без параметров. С - класс.

mem_fun(R(C::*pmf)); // R - тип возвращаемого значения функции.

// на которую ссылается указатель

Функция

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

Поделиться:
Популярные книги

Кодекс Крови. Книга VIII

Борзых М.
8. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга VIII

Камень

Минин Станислав
1. Камень
Фантастика:
боевая фантастика
6.80
рейтинг книги
Камень

Партиец

Семин Никита
2. Переломный век
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Партиец

Он тебя не любит(?)

Тоцка Тала
Любовные романы:
современные любовные романы
7.46
рейтинг книги
Он тебя не любит(?)

Бестужев. Служба Государевой Безопасности

Измайлов Сергей
1. Граф Бестужев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности

Хозяйка брачного агентства или Попаданка в поисках любви

Максонова Мария
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Хозяйка брачного агентства или Попаданка в поисках любви

Возрождение Феникса. Том 1

Володин Григорий Григорьевич
1. Возрождение Феникса
Фантастика:
фэнтези
попаданцы
альтернативная история
6.79
рейтинг книги
Возрождение Феникса. Том 1

Релокант. Вестник

Ascold Flow
2. Релокант в другой мир
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Релокант. Вестник

Книга пяти колец. Том 3

Зайцев Константин
3. Книга пяти колец
Фантастика:
фэнтези
попаданцы
аниме
5.75
рейтинг книги
Книга пяти колец. Том 3

Идеальный мир для Лекаря 8

Сапфир Олег
8. Лекарь
Фантастика:
юмористическое фэнтези
аниме
7.00
рейтинг книги
Идеальный мир для Лекаря 8

Идеальный мир для Социопата 6

Сапфир Олег
6. Социопат
Фантастика:
боевая фантастика
рпг
6.38
рейтинг книги
Идеальный мир для Социопата 6

Камень. Книга 3

Минин Станислав
3. Камень
Фантастика:
фэнтези
боевая фантастика
8.58
рейтинг книги
Камень. Книга 3

Прометей: повелитель стали

Рави Ивар
3. Прометей
Фантастика:
фэнтези
7.05
рейтинг книги
Прометей: повелитель стали

Неудержимый. Книга XIX

Боярский Андрей
19. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XIX