любой программный код в том же самом пространстве имен сможет без проблем записать ваш объект в ostream
(или считать его из istream
).
15.6. Инициализация последовательности значениями, разделяемыми запятыми
Требуется инициализировать последовательность набором значений, разделяемых запятыми, подобно тому как это делается для встроенных массивов.
При инициализации стандартных последовательностей (таких как vector
и list
) можно использовать синтаксис с запятыми, определяя вспомогательный класс и перегружая оператор запятой, как это продемонстрировано в примере 15.6.
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>
using namespace std;
template<class Seq_T>
struct comma helper {
typedef typename Seq_T::value_type value_type;
explicit comma_helper(Seq_T& x) : m(x) {}
comma_helper& operator=(const value_type& x) {
m.clear();
return operator+=(x);
}
comma_helper& operator+=(const value_type& x) {
m.push_back(x);
return *this;
}
Seq_T& m;
};
template<typename Seq_T>
comma_helper<Seq_T> initialize(Seq_T& x) {
return comma_helper<Seq_T>(x);
}
template<class Seq_T, class Scalar_T>
comma_helper<Seq_T>& operator,(comma_helper<Seq_T>& h, Scalar_T x) {
h += x;
return h;
}
int main() {
vector v;
int a = 2;
int b = 5;
initialize(v) = 0, 1, 1, a, 3, b, 8, 13;
cout << v[3] << endl; // выдает 2
system('pause');
return EXIT_SUCCESS;
}
Часто стандартные последовательности инициализируются путем вызова несколько раз функции- члена push_back
. Поскольку это приходится делать не так уж редко, я написал функцию initialize
, которая помогает избавиться от этого скучного занятия, позволяя выполнять инициализацию значениями, разделяемыми запятыми, подобно тому как это делается во встроенных массивах.
Возможно, вы и не знали, что запятая является оператором, который можно переопределять. Здесь вы не одиноки — этот факт не является общеизвестным. Оператор запятой было разрешено перегружать почти только ради решения этой задачи.
В решении используется вспомогательная функция initialize
, которая возвращает шаблон вспомогательной функции comma_helper
. Этот шаблон содержит ссылку на последовательность и перегруженные операторы operator,
, operator=
и operator+=
.
Такое решение требует, чтобы я определил отдельную вспомогательную функцию из-за особенностей восприятия компилятором оператора v = 1, 1, 2, ...;
. Компилятор рассматривает v = 1
как недопустимое подвыражение, потому что в стандартных последовательностях не поддерживается оператор присваивания единственного значения. Функция initialize
конструирует соответствующий объект comma_helper
, который может хранить последовательность, используемую в перегруженном операторе присваивания и запятой.
Оператор запятой (comma operator), называемый также оператором последовательности (sequencing operator), по умолчанию рассматривает выражения слева направо, и в результате получается значение и тип самого правого значения. Однако при перегрузке operator
принимает новый смысл и теряет первоначальную семантику. Здесь возникает один тонкий момент — оценка параметров слева направо теперь не гарантируется, и результат выполнения программного кода, приведенного в примере 15.7, может оказаться неожиданным.
int prompt_user() {
cout << 'give me an integer ... ';
cin >> n;
return n;
}
void f() {
vector<int> v;
// Следующий оператор может инициализировать v в неправильной
// последовательности
intialize(v) = prompt_user(), prompt_user();
}