C ++意外值范围-v3 partial_sum视图

时间:2017-09-09 22:58:41

标签: c++ range-v3

考虑以下最小例子:

#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<<" ";
    }
}

此输出

6 3 2 3 4 5 

我原本希望在这里看到双数,但结果显然是整数。这与view::transform的行为形成对比。

原因是因为在实现中,run-sum值的类型与源范围相对应:

semiregular_t<range_value_type_t<Rng>> sum_;

这是故意还是错误?

讨论:我看到在尝试获取有效返回类型时遇到的麻烦,因为转换函数使用源范围和结果范围作为参数并产生返回类型。下一个应用程序使用source-range-type和this返回类型来生成另一个(可能是不同的)返回类型,依此类推。

由此,原则上,一个是使用变换函数的结果类型重复链接源值类型。只有当结果类型“收敛”到可以转换为所有其他中间结果的特定类型时,此重复迭代才会产生一些可用的东西(在上面的示例中,此类型为double,这是在第一个之后获得的调用转换函数。)

通过这种观察,可以提出一种解决方法:将二进制变换函数应用给定次数,并使用common_type作为结果范围的值类型(如果发现收敛,则过早停止)。在最简单的情况下,迭代次数只有一次。如果这个迭代没有导致合理的东西,那么仍然可以求助于source-value-type(或编译器错误)。

为清楚起见,以下是上述示例的应用程序:

First iteration : f(int,int)    -> yields "double"
Second iteration: f(int,double) -> yields "double"
Third iteration : f(int,double) -> yields "double"

在第三次迭代之后,模式收敛,因此停止并选择公共类型double作为返回范围的value_type。

我不确定这种方法在所有理论情况下是否完全有效,但至少它在第一个例子中给出了两倍 - 我猜这是每个人都非常期待的。

1 个答案:

答案 0 :(得分:5)

设计

ranges::view::partial_sum反映了std::partial_sum的语义。如果您运行:

#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>

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; };
    std::vector<double> rng;
    std::partial_sum(v.begin(), v.end(), std::back_inserter(rng), f);

    for(auto i : rng)
    {
        std::cout<<i<<" ";
    }
}

你应该得到exactly the same output as from the program in the OP。与许多范围v3视图一样,此视图的作用是计算与标准算法计算的结果相同的结果序列,但这样做很懒散。

指定

std::partial_sum对累加器进行操作,其类型与输入范围的值类型相同。 [partial.sum]/2说:

  

效果:对于非空范围,该函数会创建一个累加器acc,其类型为InputIterator的值类型,使用*first初始化它,并将结果赋给{ {1}}。对于*result中的每个迭代器i[first + 1, last)然后由accacc = acc + *i进行修改,结果将分配给acc = binary_­op(acc, *i)。< / p>

为了表现相同,*(result + (i - first))还使用了一个累加器,其类型是输入范围的值类型。

对于OP,您可以使用ranges::view::partial_sum作为输入范围的类型来实现所需的结果。使用range-v3,通过使用double

进行合成,可以轻松实现
ranges::view::transform(ranges::convert_to<double>{})

which produces the desired output

6 3.2 3.06 3.718 4.6154 5.58462