valarray разделить свою стихию
Когда я делю valarray на его первый элемент, только первый элемент становится 1, а другие сохраняют свое первоначальное значение.
#include <iostream>
#include <valarray>
using namespace std;
int main() {
valarray<double> arr({5,10,15,20,25});
arr=arr/arr[0]; // or arr/=arr[0];
for(int value:arr)cout << value << ' ';
return 0;
}
Фактический результат:
1 10 15 20 25
Ожидаемый результат:
1 2 3 4 5
Почему фактический результат не соответствует ожидаемому?
Я использую g++(4.8.1) с -std= C++11
2 ответа
Детали того, почему это происходит, связаны с приемами реализации, используемыми в valarray
улучшить производительность. И libstdC++, и libC++ используют шаблоны выражений для результатов valarray
операции, а не выполнять операции немедленно. Это явно разрешено [valarray.syn] p3 в стандарте C++:
Любая функция, возвращающая
valarray<T>
разрешено возвращать объект другого типа при условии, что все функции-члены constvalarray<T>
также применимы к этому типу.
Что происходит в вашем примере, так это arr/arr[0]
не выполняет деление сразу, а вместо этого возвращает объект, такой как _Expr<__divide, _Valarray, _Constant, valarray<double>, double>
который имеет ссылку на arr
и ссылка на arr[0]
, Когда этот объект назначен другому valarray
операция деления выполняется, и результат сохраняется непосредственно в левой части назначения (это позволяет избежать создания временного valarray
сохранить результат, а затем скопировать его в левую часть).
Потому что в вашем примере левая сторона является тем же объектом, arr
, это означает, что ссылка на arr[0]
хранится в шаблоне выражения ссылается на другое значение, как только первый элемент в arr
был обновлен с результатом.
Другими словами, конечный результат выглядит примерно так:
valarray<double> arr{5, 10, 15, 20, 25};
struct DivisionExpr {
const std::valarray<double>& lhs;
const double& rhs;
};
DivisionExpr divexpr = { arr, arr[0] };
for (int i = 0; i < size(); ++i)
arr[i] = divexpr.lhs[i] / divexpr.rhs;
Первая итерация цикла for установит arr[0]
в arr[0] / arr[0]
т.е. arr[0] = 1
, а затем будут установлены все последующие итерации arr[i] = arr[i] / 1
что означает, что значения не меняются.
Я рассматриваю возможность внесения изменений в реализацию libstdC++, чтобы шаблон выражения сохранял double
непосредственно вместо того, чтобы держать ссылку. Это будет означать arr[i] / divexpr.rhs
всегда буду оценивать arr[i] / 5
и не использовать обновленное значение arr[i]
,
Этот работает:
#include <iostream>
#include <valarray>
using namespace std;
int main() {
valarray<double> arr({5,10,15,20,25});
auto v = arr[0];
arr=arr/v; // or arr/=arr[0];
for(int value:arr)cout << value << ' ';
return 0;
}
Проблема в том, что вы пытаетесь использовать значение (arr[0]
) из массива, который вы изменяете одновременно (arr
).
Интуитивно, как только вы обновили arr[0]
при выполнении arr[0]/arr[0]
какое значение оно содержит?
Ну, это значение, которое теперь будет использоваться для деления других значений...
Пожалуйста, обратите внимание, что то же самое относится к arr/=arr[0]
(прежде всего, arr[0]/arr[0]
происходит, чем все остальные, в цикле for или что-то в этом роде).
Также обратите внимание на документацию, что operator[]
из std::valarray
возвращает T&
, Это подтверждает предположение выше: оно обращено к 1
В качестве первого шага вашей итерации все остальные операции бесполезны.
Простое копирование решает проблему, как в примере кода.