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

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

vector 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 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

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 // Объявление mem_fun для неконстантных

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

mem_fun(R(C::*pmf)); // 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.

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

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