На этом шаге мы рассмотрим передачу объектов функций по ссылке.
Объекты функций передаются по значению, а не по ссылке. Следовательно, алгоритм не изменяет состояния объекта функции. Например, следующий код генерирует последовательность, начинающуюся со значения 1, дважды:
IntSequence seq(1); // Серия целых чисел, начинающаяся с 1 // Вставка последовательности, начинающейся с 1 generate_n (back_inserter(coll), 9, seq); // Повторная вставка последовательности, начинающейся с 1 generate_n (back_inserter(coll), 9, seq);
Передача объектов функций по значению, а не по ссылке, хороша тем, что позволяет передавать константы и временные выражения. В противном случае передача конструкции IntSequence(1) было бы невозможна.
Впрочем, у передачи объектов функций по значению есть и недостатки: такая передача не позволяет учесть изменения в состоянии объекта. Алгоритм может изменить состояние объекта функции, однако вам не удастся получить итоговое состояние и обработать его в программе, потому что алгоритм создает внутреннюю копию объекта функции. Но что делать, если вам все-таки необходимо получить "результат" от алгоритма?
Существуют два способа получения "результата" при использовании объектов функций алгоритмами:
Чтобы передать объект функции по ссылке, достаточно уточнить вызов алгоритма так, чтобы тип объекта функции представлял собой ссылку. Пример:
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include <iterator> #include <list> #include <algorithm> #include <conio.h> //необходимо для getch() #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused using namespace std; std::string ToRus(const std::string &in) { char *buff = new char [in.length()+1]; CharToOem(in.c_str(),buff); std::string out(buff); delete [] buff; return out; } template <class T> inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="") { typename T::const_iterator pos; std::cout << ToRus(optcstr); for (pos=coll.begin(); pos!=coll.end(); ++pos) { std::cout <<*pos <<' '; } std::cout << std::endl; } class IntSequence { private: int value; public: // Конструктор IntSequence (int initialValue) : value(initialValue) { } // "Вызов функции" int operator() () { return value++; } }; int main(int argc, char* argv[]) { list<int> coll; IntSequence seq(1); // Серия целых чисел, начинающаяся с 1 // Вставка значений от 1 до 4 // - передача объекта функции по ссылке, // чтобы при следующем вызове значения начинались с 5 generate_n<back_insert_iterator<list<int> >, int, IntSequence&>(back_inserter(coll), // Начало 4, // Количество элементов seq); // Генератор значений PRINT_ELEMENTS(coll,"Исходный список:\n"); // Вставка значений от 42 до 45 generate_n (back_inserter(coll), // Начало 4, // Количество элементов IntSequence(42)); // Генератор значений PRINT_ELEMENTS(coll,"Список после добавления:\n"); // Продолжение первой последовательности // - передача объекта функции по ссылке, // чтобы при следующем вызове значения снова начинались с 5 generate_n (back_inserter(coll), // Начало 4, // Количество элементов seq); // Генератор значений PRINT_ELEMENTS(coll,"Список (передача объекта функции по ссылке):\n"); // Снова продолжить первую последовательность generate_n (back_inserter(coll), // Начало 4, // Количество элементов seq); // Генератор значений PRINT_ELEMENTS(coll,"Результат:\n"); getch(); return 0; } //---------------------------------------------------------------------------
Результат выполнения программы выглядит так:
Рис.1. Результат работы приложения
При первом вызове generate_n() объект функции seq передается по ссылке, для чего производится уточнение аргументов шаблона:
generate_n<back_insert_iterator<list<int> >, int, IntSequence&>(back_inserter(coll), // Начало 4, // Количество элементов seq); // Генератор значений
В результате внутреннее значение seq изменяется после вызова, а второе использование seq при третьем вызове generate_n() продолжает серию из первого вызова. Но на этот раз seq передается по значению, а не по ссылке:
generate_n (back_inserter(coll), // Начало 4, // Количество элементов seq); // Генератор значений
Следовательно, вызов не изменяет внутреннего состояния seq, поэтому следующий вызов generate_n() снова продолжает серию, начиная с 5.
На следующем шаге мы рассмотрим возвращаемое значение алгоритма for_each.