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

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

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

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

for_each(vw.begin(),vw.end(),

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

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

list lpw:// Список lpw содержит указатели

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

for_each(lpw.begin(),lpw.end(),

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

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

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

template

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

{

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

}

Жирный шрифт используется для выделения того, что при вызове foreach используется синтаксис 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 // Объявление mem_fun для неконстантных

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

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

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

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

list 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_def адаптируют синтаксис 2 к синтаксису 1 и генерируют адаптеры типа mem_fun_left.

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

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

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

Перейти на страницу:

Все книги серии Библиотека программиста

Похожие книги