Consider the following minimal example:
#include <range/v3/all.hpp>
#include <iostream>
namespace rng = ranges::v3;
int main()
{
std::vector<int> v { 6, 2, 3, 4, 5, 6 };
auto f = [](auto a, auto b) { return a*0.3 + b*0.7;};
auto rng = v | rng::view::partial_sum(f);
for(auto i : rng)
{
std::cout<<i<<" ";
}
}
This outputs
6 3 2 3 4 5
I would have expected to see double numbers here, but the result are obvisouly integers. This is in contrast to the behavior of view::transform
.
The reason for this is because in the implementation, the running-sum value has a type that corresponds to the source range:
semiregular_t<range_value_type_t<Rng>> sum_;
Is this intended or a bug?
Discussion: I see the trouble one is running into when trying to get a valid return type, as as the transformation function is using both the source range and the result range as parameters and produces a return type. The next application uses the source-range-type and this return type to produce another (possibly different) return type, and so on.
By this, in principle, one is repeatedly chaining the source-value-type with the result types of the transformation function. This repeated iteration yields something usable only if the result type "converges" to a specific type to which all other intermediate results can be converted to (in the example above, this type is double
, which is obtained already after the first call of the transformation function).
With this observation one could propose a workaround: apply the binary transformation function a given number of times and the use the common_type
as value type of the resulting range (if one finds a convergence, prematurely stop). In the simplest case, the number of iterations is just one. If this iteration does not lead to something reasonable, one can still resort to the source-value-type (or a compiler error).
To make it clear, here is the application for the example above:
First iteration : f(int,int) -> yields "double"
Second iteration: f(int,double) -> yields "double"
Third iteration : f(int,double) -> yields "double"
After the third iteration the pattern converges, so stop and choose the common-type double
as the value_type of the returned range.
I'm not sure whether this approach is completely valid in all theoretical circumstances, but at least it gives a double in the first example -- which I guess is what everyone is strongly expecting.
ranges::view::partial_sum
by design mirrors the semantics ofstd::partial_sum
. If you run:You should get exactly the same output as from the program in the OP. Like many range-v3 views, this view's job is to compute the same sequence of results as computed by a standard algorithm, but do so lazily.
std::partial_sum
is specified to operate on an accumulator whose type is the same is the value type of the input range. [partial.sum]/2 says:To behave equivalently,
ranges::view::partial_sum
also uses an accumulator whose type is the value type of the input range.In the case of the OP, you can achieve the desired result by using
double
as the type of the input range. With range-v3 this is easy to do on-the-fly by composing withranges::view::transform(ranges::convert_to<double>{})
:which produces the desired output: