15.6. Инициализация последовательности значениями, разделяемыми запятыми
15.6. Инициализация последовательности значениями, разделяемыми запятыми
Проблема
Требуется инициализировать последовательность набором значений, разделяемых запятыми, подобно тому как это делается для встроенных массивов.
Решение
При инициализации стандартных последовательностей (таких как vector и list) можно использовать синтаксис с запятыми, определяя вспомогательный класс и перегружая оператор запятой, как это продемонстрировано в примере 15.6.
Пример 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, может оказаться неожиданным.
Пример 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();
}
В правильном варианте функции f каждый вызов prompt_user должен был бы выполняться в отдельном операторе.
Библиотека Boost Assign, написанная Торстеном Оттосеном (Thorsten Ottosen), кроме других форм инициализации стандартных коллекций поддерживает также более сложную форму инициализации списком с запятыми. Эта библиотека доступна на сайте http://www.boost.org.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Управление значениями
Управление значениями Для перечисления значений параметров открытого раздела реестра используется функция RegEnumValue. Значение параметра dwIndex должно устанавливаться равным 0 при первом вызове функции и увеличиваться на единицу при каждом последующем вызове. После
Работа с постоянными значениями
Работа с постоянными значениями Если в программе есть значения, которые не должны меняться, то для представления таких значений не обязательно создавать переменные. Конечно, в любой процедуре для представления этих значений можно использовать и буквальные значения, но
Типы, характеризуемые значениями, ссылочные типы и оператор присваивания
Типы, характеризуемые значениями, ссылочные типы и оператор присваивания Теперь изучите следующий метод Main() и рассмотрите его вывод, показанный на рис. 3.12.static void Main(string[] args) { Console.WriteLine("*** Типы, характеризуемые значением / Ссылочные типы ***"); Console.WriteLine(-› Создание p1"); MyPoint
Типы, характеризуемые значениями, и ссылочные типы: заключительные замечания
Типы, характеризуемые значениями, и ссылочные типы: заключительные замечания Чтобы завершить обсуждение данной темы, изучите информацию табл. 3.8, в которой приводится краткая сводка основных отличий между типами, характеризуемыми значением, и ссылочными типами.Таблица
Управляющие последовательности
Управляющие последовательности Как и в других языках, подобных C, строковые литералы в C# могут содержать различные управляющие последовательности, которые интерпретируются как определенный набор данных, предназначенных для отправки в выходной поток. Каждая
9.4.4. Аргументы со значениями по умолчанию
9.4.4. Аргументы со значениями по умолчанию Наличие аргументов со значениями по умолчанию способно расширить множество устоявших функций. Устоявшими являются функции, которые вызываются с данным списком фактических аргументов. Но такая функция может иметь больше
Операции с булевыми значениями
0
Последовательности
Последовательности Последовательность - это набор данных, которые можно перебрать один за другим в некотором порядке. К разновидностям последовательностей относятся одномерные динамические массивы array of T, списки List<T>, двусвязные списки LinkedList<T>, множества
Последовательности команд
Последовательности команд Часто для выполнения определенного действия пользователь должен по очереди раскрывать несколько пунктов меню. Например, чтобы запустить в Windows Vista программу Блокнот, нужно выполнить следующие действия.1. Нажать кнопку Пуск.2. Выбрать пункт Все