Вход/Регистрация
Эффективное использование STL
вернуться

Мейерс Скотт

Шрифт:

Помните, что базовые классы

unary_function
и
binary_function
выполняют только одну важную функцию — они предоставляют определения типов, необходимые для работы адаптеров, поэтому наследование от этих классов порождает адаптируемые объекты функций. Это позволяет использовать в программах следующие конструкции:

list<Widget> widgets;

…

list<Widget>::reverse_iterator i1 = // Найти последний объект

 find_if(widgets.rbegin, widgets.rend, // Widget, не соответствующий

 not1(MeetsThreshold<int>(10))); // пороговому критерию 10

//(что бы это ни означало)

Widget w(аргументы конструктора); // Найти первый объект Widget.

list<Widget>::iterator i2 = // предшествующий w в порядке

 find_if(widgets.begin, widgets.end, // сортировки, определенном

 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 (не компилируется!)

  • Читать дальше
  • 1
  • ...
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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