Reputation: 143
I am using the type std::valarray<std::valarray<double>>
and wish to sum each of the contained valarrays element wise, to leave a std::valarray<double>
.
The C++ documentation states that the operator .sum() can be applied to std::valarray<T>
so long as the operator +=
is defined for type T. My code below (method1) tries to apply this to std::valarray<std::valarray<double>>
, but the result appears to be nonsense.
However if I perform this manually, using the +=
operator (method2), I get the result I want. But the fact that method2 works seems to imply that the operator +=
is defined for the type std::valarray<double>
, and hence that method1, using .sum(). should work. I really can't understand what is happening here...
My code:
#include <iostream>
#include <valarray>
// Attempt to use .sum() operator
std::valarray<double> method1(const std::valarray<std::valarray<double>>& data) {
return data.sum();
}
// Manual summation using += operator
std::valarray<double> method2(const std::valarray<std::valarray<double>>& data) {
std::valarray<double> sum(data[0].size());
for (size_t i{0}; i < data.size(); i++) {
sum += data[i];
}
return sum;
}
// Display size and elements
void showData(const std::valarray<double> data) {
std::cout << "Size = " << data.size() << "\n";
std::cout << "Data = ";
for (size_t i{0}; i < data.size(); i++) {
std::cout << data[i] << " ";
}
std::cout << "\n\n";
}
int main() {
std::valarray<std::valarray<double>> data{{1,2},{3,4}};
showData(method1(data));
showData(method2(data));
}
My output:
Size = 0
Data =
Size = 2
Data = 4 6
Upvotes: 4
Views: 690
Reputation: 13269
The sum
method of std::valarray
requires operator+=
to be defined for its value type (in your case, std::valarray
), but std::valarray
also requires it to be default-constructible (from the "Numeric" concept requirement).
This allows the sum
method to work without operator+
, by first default-constructing an element, and then adding each contained element with operator+=
.
Although it isn't defined anywhere, as far as I know, it probably works something like this.
T sum() const {
T result;
for (auto& it : elements) {
result += it;
}
return result;
}
The problem with a valarray of valarrays (std::valarray<std::valarray>
) is that a default-constructed valarray is empty. And when operator+=
is applied with an empty valarray and a non-empty one, it results in undefined behavior ("The behavior is undefined if size() != v.size()
"). What you are likely to get is an empty valarray as a result (but you could potentially get anything).
What you could use instead is std::accumulate
. It requires an initial value as third parameter, which takes care of the problem.
std::accumulate(std::begin(data), std::end(data), std::valarray<double>(data[0].size()))
PS: don't ask me why std::valarray
has no method begin
and end
.
Upvotes: 2