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

Мейерс Скотт

Шрифт:

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

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
. Например, в следующем фрагменте:

list<Widget*> lpw; // См. ранее

…

for_each(lpw.begin, lpw.end,

 mem_fun(&Widget::test)); // Теперь нормально компилируется

При вызове

for_each
передается объект типа
mem_fun_t
, содержащий указатель на
Widget::test
. Для каждого указателя
Widget*
в
lpw
алгоритм
for_each
«вызывает» объект
mem_fun_t
с использованием синтаксиса 1, а этот объект непосредственно вызывает
Widget::test
для указателя
Widget*
с использованием синтаксиса 3.

В целом

mem_fun
приводит синтаксис 3, необходимый для
Widget::test
при использовании с указателем
Widget*
, к синтаксису 1, используемому алгоритмом
for_each
. По вполне понятным причинам такие классы, как
mem_fun_t
, называются адаптерами объектов функций. Наверное, вы уже догадались, что по аналогии со всем, о чем говорилось ранее, функции
mem_fun_ref
адаптируют синтаксис 2 к синтаксису 1 и генерируют адаптеры типа
mem_fun_ref_t
.

Объекты, создаваемые функциями

mem_fun
и
mem_fun_ref
, не ограничиваются простой унификацией синтаксиса для компонентов STL. Они (а также объекты, создаваемые функцией
ptr_fun
) также предоставляют важные определения типов. Об этих определениях уже было рассказано в совете 40, поэтому я не стану повторяться. Тем не менее, стоит разобраться, почему конструкция

for_each(vw.begin, vw.end, test); // См. ранее, вариант 1.

// Нормально компилируется

компилируется, а следующие конструкции не компилируются:

for_each(vw.begin, vw.end, &Widget::test); // См. ранее, вариант 2.

// Не компилируется.

for_each(lpw.begin, lpw.end, &Widget::test); // См. ранее, вариант 3.

// Не компилируется

При первом вызове (вариант 1) передается настоящая функция, поэтому адаптация синтаксиса вызова для

for_each
не нужна; алгоритм сам вызовет ее с правильным синтаксисом. Более того,
for_each
не использует определения типов, добавляемые функцией
ptr_fun
, поэтому при передаче
test
функция
ptr_fun
не нужна. С другой стороны, добавленные определения не повредят, поэтому следующий фрагмент функционально эквивалентен приведенному выше:

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

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

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

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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