На этом шаге мы рассмотрим использование функциональных адаптеров для функций классов.
В стандартную библиотеку C++ включены дополнительные адаптеры, позволяющие вызвать некоторую функцию класса для каждого элемента коллекции (таблица 1).
Выражение | Описание |
---|---|
mem_fun_ref(op) | Вызов ор() как функции объекта |
mem_fun (op) | Вызов ор() как функции указателя на объект |
В следующем примере адаптер mem_fun_ref вызывает функцию print() каждого элемента вектора:
class Person { private: std::string name; public: ... void print () const { std::cout << name << std::endl; } void printWithPrefix (std::string prefix) const { std::cout << prefix << name << std::endl; } }; void foo (const std::vector<Person>& coll) { using std::for_each; using std::bind2nd; using std::mem_fun_ref; // Вызов функции print() для каждого элемента вектора for_each (coll.begin(), coll.end(), mem_fun_ref(&Person::print)); // Вызов функции printWithPrefix() для каждого элемента // - строка "person: " передается при вызове for_each (coll.begin(), coll.end(), bind2nd(mem_fun_ref(&Person::printWithPrefix), "person: ")); } int main() { std::vector<Person> coll(5); foo(coll); std::vector<Person*> coll2; coll2.push_back(new Person); ptrfoo(coll2); }
В функции foo() для каждого элемента вектора coll вызываются две функции класса Person: '
Чтобы вызвать функцию Person::print(), мы передаем объект функции mem_fun_ref(&Person::print) алгоритму for_each():
for_each (coll.begin(), coll.end(), mem_fun_ref(&Person::print));
Адаптер mem_fun_ref трансформирует обращение к элементу в вызов указанной функции класса.
Почему необходимо использовать адаптер? Потому что алгоритму нельзя напрямую передать функцию класса. Если попытаться это сделать, произойдет ошибка компиляции:
for_each (coll.begin(), coll.end(), &Person::print); // ОШИБКА: невозможно вызвать оператор () // для указателя на функцию класса
Проблема заключается в том, что for_each() вызывает оператор () для указателя, переданного в третьем аргументе, вместо функции класса, на которую он ссылается. Адаптер mem_fun_ref решает эту проблему, преобразуя вызов оператора ().
Как показывает второй вызов for_each(), адаптер bind2nd также позволяет передать один аргумент вызванной функции класса:
for_each (coll.begin(), coll.end(), bind2nd(mem_fun_ref(&Person::printWithPrefix), "person: "));
Наверное, вас удивляет, что адаптер называется mem_fun_ref, а не просто mem_fun. Исторически сложилось так, что первой появилась другая версия функциональных адаптеров, которой и было присвоено название mem_fun. Адаптеры mem_fun предназначались для последовательностей, содержащих указатели на элементы. Возможно, во избежание путаницы их следовало бы назвать mem_ fun_ptr. Следовательно, функции классов могут вызываться не только для серий объектов, но и для серий указателей на объекты. Пример:
void ptrfoo (const std::vector<Person*>& coll) // ^^^ указатель! { using std::for_each; using std::bind2nd; using std::mem_fun; // Вызов функции print() для каждого объекта, // на который ссылается указатель for_each (coll.begin(), coll.end(), mem_fun(&Person::print)); // Вызов функции printWithPrefix () для каждого объекта, // на который ссылается указатель // - строка "person: " передается при вызове for_each (coll.begin(), coll.end(), bind2nd(mem_fun(&Person::printWithPrefix), "person: ")); }
Адаптеры mem_fun_ref и mem_fun позволяют вызывать функции классов без аргументов или с одним аргументом. Вызвать функцию с двумя аргументами подобным образом не удастся. Дело в том, что для реализации этих адаптеров необходимы вспомогательные объекты функций, предоставляемые для каждого вида функций. Например, вспомогательные классы для mem_fun и mem_fun_ref называются mem_fun_t, mem_fun_ref_t, const_mem_fun_t, const_mem_fun_ref_t, mem_ funl_t, mem_funl_ref_t, const_mem_funl_t и const_mem_funl_ref_t.
Учтите, что функции классов, вызываемые mem_fun_ref и mem_fun, должны быть константными. К сожалению, стандартная библиотека C++ не предоставляет функциональных адаптеров для неконстантных функций классов.
На следующем шаге мы рассмотрим функциональные адаптеры для обычных функций.