Теперь понятно, для чего нужны функции 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.
В целом 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
не нужна. С другой стороны, добавленные определения не повредят, поэтому следующий фрагмент функционально эквивалентен приведенному выше:
for_each(vw.begin(), vw.end(), ptr_fun(test)); // Компилируется и работает,
// как вариант 1.
Если вы забываете, когда функция ptr_fun
обязательна, а в каких случаях без нее можно обойтись, лучше используйте ее при всех передачах функций компонентам STL. STL игнорирует лишние вызовы, и они не отражаются на быстродействии программы. Возможно, во время чтения вашей программы кто-нибудь удивленно поднимет брови при виде лишнего вызова ptr_fun
. Насколько это беспокоит вас? Наверное, ответ зависит от природной мнительности.
Существует и другой подход — использовать ptr_fun
в случае крайней необходимости. Если функция отсутствует там, где необходимы определения типов, компилятор выдает сообщение об ошибке. Тогда вы возвращаетесь к программе и включаете в нее пропущенный вызов.
С mem_fun
и mem_fun_ref
ситуация принципиально иная. Эти функции всегда должны применяться при передаче функции компонентам STL, поскольку помимо определения типов (необходимых или нет) они адаптируют синтаксис вызова, который обычно используется для функций класса, к синтаксису, принятому в STL. Если не использовать эти функции при передаче указателей на функции класса, программа не будет компилироваться.
Читать дальше