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> разрешено возвращать объект другого типа при условии, что все функции-члены const valarray<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 В качестве первого шага вашей итерации все остальные операции бесполезны.
Простое копирование решает проблему, как в примере кода.

Другие вопросы по тегам