Копирование всех элементов выполняется как обычно.
Matrix<int> a2 = a; // копирующая инициализация
a = a2; // копирующее присваивание
К каждому элементу объекта класса
Matrix
можно применять встроенные операции.
a *= 7; // пересчет: a[i]*=7 для каждого i (кроме того, +=, –=, /=
// и т.д.)
a = 7; // a[i]=7 для каждого i
Это относится к каждому оператору присваивания и каждому составному оператору присваивания (
=
,
+=
,
–=
,
/=
,
*=
,
%=
,
^=
,
&=
,
|=
,
>>=
,
<<=
) при условии, что тип элемента поддерживает соответствующий оператор. Кроме того, к каждому элементу объекта класса
Matrix
можно применять функции.
a.apply(f); // a[i]=f(a[i]) для каждого элемента a[i]
a.apply(f,7); // a[i]=f(a[i],7) для каждого элемента a[i]
Составные операторы присваивания и функция
apply()
модифицируют свои аргументы типа
Matrix
. Если же мы захотим создать новый объект класса
Matrix
, то можем выполнить следующую инструкцию:
b = apply(abs,a); // создаем новый объект класса Matrix
// с условием b(i)==abs(a(i))
Функция
abs
— это стандартная функция вычисления абсолютной величины (раздел 24.8). По существу, функция
apply(f,x)
связана с функцией
x.apply(f)
точно так же, как оператор
+
связан с оператором
+=
. Рассмотрим пример.
b = a*7; // b[i] = a[i]*7 для каждого i
a *= 7; // a[i] = a[i]*7 для каждого i
y = apply(f,x); // y[i] = f(x[i]) для каждого i
x.apply(f); // x[i] = f(x[i]) для каждого i
В результате
a==b
и
x==y
.
В языке Fortran второй вариант функции
apply
называется
функцией пересылки (“broadcast” function). В этом языке чаще пишут вызов
f(x)
, а не
apply(f,x)
. Для того чтобы эта возможность стала доступной для каждой функции
f
(а не только для отдельных функций, как в языке Fortran), мы должны присвоить операции пересылки конкретное имя, поэтому (повторно) использовали имя apply.
Кроме того, для того чтобы обеспечить соответствие с вариантом функции-члена
apply
, имеющим вид
a.apply(f,x)
, мы пишем
b = apply(f,a,x); // b[i]=f(a[i],x) для каждого i
Рассмотрим пример.
double scale(double d, double s) { return d*s; }
b = apply(scale,a,7); // b[i] = a[i]*7 для каждого i
Обратите внимание на то, что “автономная” функция
apply()
принимает в качестве аргумента функцию, вычисляющую результат по ее аргументам, а затем использует этот результат для инициализации итогового объекта класса
Matrix
. Как правило, это не приводит к изменению объекта класса
Matrix
, к которому эта функция применяется. В то же время функция-член
apply()
отличается тем, что принимает в качестве аргумента функцию, модифицирующую ее аргументы; иначе говоря, она модифицирует элементы объекта класса
Matrix
, к которому применяется. Рассмотрим пример.
void scale_in_place(double& d, double s) { d *= s; }
b.apply(scale_in_place,7); // b[i] *= 7 для каждого i
В классе
Matrix
предусмотрено также много полезных функций из традиционных математических библиотек.
Matrix<int> a3 = scale_and_add(a,8,a2); // объединенное умножение
// и сложение
int r = dot_product(a3,a); // скалярное произведение
Операцию
scale_and_add()
часто называют
объединенным умножением и сложением (fused multiply-add), или просто
fma; ее определение выглядит так:
result(i)=arg1(i)*arg2+arg3(i)
для каждого
i
в объекте класса
Matrix
. Скалярное произведение также известно под именем
inner_product
и описано в разделе 21.5.3; ее определение выглядит так:
result+=arg1(i)*arg2(i)
для каждого
i
в объекте класса
Matrix
, где накопление объекта
result
начинается с нуля.
Одномерные массивы очень широко распространены; их можно представить как в виде встроенного массива, так и с помощью классов
vector
и
Matrix
. Класс
Matrix
следует применять тогда, когда необходимо выполнять матричные операции, такие как
*=
, или когда объект класса
Matrix
должен взаимодействовать с другими объектами этого класса, имеющими более высокую размерность.
Полезность этой библиотеки можно объяснить тем, что она лучше согласована с математическими операциями, а также тем, что при ее использовании не приходится писать циклы для работы с каждым элементом матрицы. В любом случае в итоге мы получаем более короткий код и меньше возможностей сделать ошибку. Операции класса
Matrix
, например копирование, присваивание всем элементам и операции над всеми элементами, позволяют не использовать циклы (а значит, можно не беспокоиться о связанных с ними проблемах).