C++20 std::views::filter未正确过滤视图问题求助
C++正态分布随机数过滤后输出异常问题及优化方案
问题描述
编写了一个C++程序,从均值为-4、标准差为15的正态分布生成随机整数值,取前1005个生成项并过滤出绝对值大于42的数值。但添加view::filter后,输出的却是不符合条件的随机值(如-12 -8 -14 2 -4 -11 -38);移除filter时,序列正常,且约有3个符合条件的值。
编译环境:使用g++ -o test main.cpp -std=c++2b在Windows和Linux编译;替换views::repeat(0)为views::iota(0)并以-std=c++20编译,问题仍存在。
原始代码
int main() { //convenience namespace rng = std::ranges; namespace view = std::views; //random generation boilerplate std::random_device r; std::mt19937 gen(r()); std::normal_distribution<float> taps(-4, 15); //the filtering for (const auto& value : view::repeat(0) | view::transform([&taps, &gen](auto _) { return std::round(taps(gen)); }) | view::take(1005) | view::filter([](auto val) { return (val < -42 || val > 42); })) { std::cout << value << ' '; } return 0; }
问题排查与解决
1. 验证过滤逻辑执行
在filter的lambda中加入调试输出,确认每个值的判断结果,排查是否存在逻辑执行异常:
| view::filter([](auto val) { bool is_valid = (val < -42 || val > 42); std::cerr << "Check value: " << val << " -> " << (is_valid ? "keep" : "discard") << '\n'; return is_valid; })
如果发现不符合条件的值被错误保留,大概率是旧版本g对C20/23视图管道的支持存在bug。
2. 规避视图管道潜在问题
先将生成的1005个值存入容器,再进行过滤,绕过视图链式调用的可能bug:
#include <iostream> #include <random> #include <ranges> #include <vector> #include <cmath> int main() { namespace rng = std::ranges; namespace view = std::views; std::random_device r; std::mt19937 gen(r()); std::normal_distribution<float> taps(-4, 15); // 先生成前1005个值到容器 std::vector<double> values; values.reserve(1005); rng::generate_n(std::back_inserter(values), 1005, [&]() { return std::round(taps(gen)); }); // 过滤并输出符合条件的值 for (const auto& val : values | view::filter([](auto v) { return v < -42 || v > 42; })) { std::cout << val << ' '; } return 0; }
3. 升级编译器版本
如果是编译器bug导致的问题,建议升级g到13.x及以上版本,新版本对C20/23标准库的支持更完善。
更优的生成序列方法
使用view::generate替代view::repeat(0) + view::transform,前者是专门用于生成无限序列的视图,语义更清晰,代码更简洁:
#include <iostream> #include <random> #include <ranges> #include <cmath> int main() { namespace rng = std::ranges; namespace view = std::views; std::random_device r; std::mt19937 gen(r()); std::normal_distribution<float> taps(-4, 15); for (const auto& value : view::generate([&]() { return std::round(taps(gen)); }) | view::take(1005) | view::filter([](auto val) { return val < -42 || val > 42; })) { std::cout << value << ' '; } return 0; }
如果需要生成1005个符合条件的值(而非先取1005个再过滤),只需调整视图顺序,将filter放在take之前:
for (const auto& value : view::generate([&]() { return std::round(taps(gen)); }) | view::filter([](auto val) { return val < -42 || val > 42; }) | view::take(1005)) { std::cout << value << ' '; }
内容的提问来源于stack exchange,提问作者God I Am Clown
相关产品推荐
相关产品推荐

