range-v3 partial_sum 视图的 C++ 意外值类型

C++ unexpected value type of range-v3 partial_sum view

考虑以下最小示例:

#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.

的行为相反

之所以这样,是因为在实现中,运行-sum 值有一个对应源范围的类型:

semiregular_t<range_value_type_t<Rng>> sum_;

这是故意的还是错误?


讨论:我看到麻烦是 运行 试图获得有效的 return 类型时,因为转换函数同时使用了两者源范围和结果范围作为参数并生成 return 类型。下一个应用程序使用 source-range-type 和这个 return 类型来生成另一个(可能不同的)return 类型,依此类推。

这样一来,原则上就是反复将源值类型与转换函数的结果类型链接起来。仅当结果类型 "converges" 为所有其他中间结果都可以转换为的特定类型时,此重复迭代才会产生可用的东西(在上面的示例中,此类型为 double,它已在之后获得转换函数的第一次调用)。

根据这一观察,可以提出一种解决方法:将二进制转换函数应用给定次数,并使用 common_type 作为结果范围的值类型(如果发现收敛,则过早停止) .在最简单的情况下,迭代次数仅为一次。如果此迭代没有导致某些合理的事情,仍然可以求助于源值类型(或编译器错误)。

为了清楚起见,这里是上面例子的应用:

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

第三次迭代后模式收敛,因此停止并选择普通类型 double 作为 returned 范围的 value_type。

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

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。与许多 range-v3 视图一样,此视图的工作是计算与标准算法计算的结果序列相同的结果序列,但这样做是懒惰的。

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

Effects: For a non-empty range, the function creates an accumulator acc whose type is InputIterator's value type, initializes it with *first, and assigns the result to *result. For every iterator i in [first + 1, last) in order, acc is then modified by acc = acc + *i or acc = binary_­op(acc, *i) and the result is assigned to *(result + (i - first)).

为了等效地表现,ranges::view::partial_sum 还使用了一个累加器,其类型是输入范围的值类型。

在 OP 的情况下,您可以通过使用 double 作为输入范围的类型来实现所需的结果。使用 range-v3,这很容易通过组合 ranges::view::transform(ranges::convert_to<double>{}):

即时完成
#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::transform(rng::convert_to<double>{}) |
        rng::view::partial_sum(f);

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

which produces the desired output:

6 3.2 3.06 3.718 4.6154 5.58462