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
考虑以下最小示例:
#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 isInputIterator
's value type, initializes it with*first
, and assigns the result to*result
. For every iteratori
in[first + 1, last)
in order,acc
is then modified byacc = acc + *i
oracc = 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