На этом шаге мы рассмотрим использование срезов.
Срез определяется набором индексов, который характеризуется тремя свойствами:
Порядок передачи этих трех свойств точно соответствует порядку следования параметров конструктора класса slice. Например, следующее выражение определяет четыре элемента, начиная с индекса 2, находящихся на расстоянии 3 друг от друга:
std::slice(2,4,3)
Другими словами, выражение определяет такой набор индексов:
2 5 8 11
Шаг может быть отрицательным. Например, рассмотрим следующее выражение:
std::slice(9,5,-2)
Это выражение определяет такой набор индексов:
9 7 5 3 1
Чтобы определить подмножество элементов массива значений, достаточно передать срез в качестве аргумента оператора индексирования. Например, следующее выражение определяет подмножество массива va, содержащее элементы с индексами 2, 5, 8 и 11:
va[std::slice(2,4,3)]
Перед вызовом необходимо проверить правильность всех индексов.
Если подмножество, заданное в виде среза, принадлежит константному массиву значений, то оно образует новый массив значений. Если массив значений не является константным, то подмножество предоставляет ссылочную семантику для работы с исходным массивом значений. Для этой цели определяется вспомогательный класс slice_array:
namespace std {
class slice;
template <class T>
class slice_array;
template <class T>
class valarray {
public:
// Срез константного массива значений
// возвращает новый массив значений
valarray<T> operator[] (slice) const;
// Срез неконстантного массива значений возвращает slice_array
slice_array<T> operator[] (slice);
...
};
}
Для объектов slice_array определены следующие операции:
Для остальных операций подмножество необходимо преобразовать в массив значений (смотри 433 шаг). Учтите, что класс slice_array проектировался исключительно как внутренний вспомогательный класс для работы со срезами, поэтому он должен оставаться невидимым для внешних пользователей. По этой причине все конструкторы и операторы присваивания класса slice_array<> объявлены закрытыми.
Например, представленная ниже команда присваивает 2 третьему, шестому, девятому и двенадцатому элементам массива значений va:
va[std::slice(2,4,3)] = 2;
Она эквивалентна следующему набору команд:
va[2] = 2; va[5] = 2; va[8] = 2; va[11] = 2;
Другая команда возводит в квадрат элементы с индексами 2, 5, 8 и 11:
va[std::slice(2,4,3)] *= std::valarray<double>(va[std::slice(2,4,3)]);
Как упоминалось на 433 шаге, следующая запись является ошибочной:
va[std::slice(2,4,3)] *= va[std::slice(2,4,3)]; // ОШИБКА
Но если воспользоваться шаблонной функцией VA() (смотри 433 шаг), запись принимает следующий вид:
va[std::slice(2,4,3)] *= VA(va[std::slice(2,4,3)]); // OK
Передавая разные срезы одного массива значений, можно объединить подмножества и сохранить результат в другом подмножестве массива. Например, рассмотрим такую команду:
va[std::slice(0,4,3)] = VA(va[std::slice(1,4,3)]) *
VA(va[std::slice(*2,4,3)]);
Эта команда эквивалентна следующему набору команд:
va[0] = va[1] * va[2]; va[3] = va[4] * va[5]; va[6] = va[7] * va[8]; va[9] = va[10] * va[11];
Если рассматривать массив значений как двухмерную матрицу, этот пример представляет собой не что иное, как умножение векторов (рисунок 1).

Рис.1. Умножение векторов с использованием срезов
Однако следует учитывать, что порядок выполнения отдельных присваиваний не определен. Следовательно, если приемное подмножество содержит элементы, используемые в исходном подмножестве, последствия могут быть непредсказуемыми.
Также возможны и более сложные команды. Например:
va[std::slice(0,100,3)]
= std::pow(VA(va[std::slice(l,100,3)]) * 5.0,
VA(va[std::slice(2,100,3)]));
Еще раз обратите внимание: отдельное значение (5.0 в данном случае) должно точно соответствовать типу элементов массива значений.
На следующем шаге мы приведем пример использования срезов.