Шаг 246.
Библиотека STL.
Объекты функций STL. Функциональные адаптеры для функций классов

    На этом шаге мы рассмотрим использование функциональных адаптеров для функций классов.

    В стандартную библиотеку C++ включены дополнительные адаптеры, позволяющие вызвать некоторую функцию класса для каждого элемента коллекции (таблица 1).

Таблица 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++ не предоставляет функциональных адаптеров для неконстантных функций классов.

    На следующем шаге мы рассмотрим функциональные адаптеры для обычных функций.




Предыдущий шаг Содержание Следующий шаг