C++標準ライブラリ提供レンジアダプタstd::views::filter適用後の要素に対する変更操作には十分留意すること。
変更操作により要素がフィルタ条件を満たさなくなる場合、C++ライブラリ仕様上は未定義動作(undefined behavior)を引き起こす。この問題は遅延評価によりフィルタ条件が複数評価されるケースで初めて表面化するため、ライブラリ仕様違反が潜在化するリスクが高い。C++ Ranges難しい(´・ω・)(・ω・`)ネー
- 2025-07-24追記:本記事で取り上げる問題は提案文書(PDF)P3329R0でも議論され、
より安全なレンジアダプタC++2d(C++29)向け提案文書(PDF)P3725R1が検討されている。後方互換性のためstd::views::input_filterを追加するstd::views::filter動作仕様は維持される見込み。*1 - 2026-05-15追記:2026年3月会合で(PDF)P3725R3+(PDF)P3828R1が採択され、
std::view::as_input | std::views::filter(pred)と記述すれば問題回避が可能となる(P3725R1当時のstd::views::input_filterに相当)。結局プログラマの責任は残る ;y=ー( ゚д゚)・∵.
#include <iostream> #include <ranges> #include <vector> bool is_odd(int x) { return x % 2 != 0; } std::vector vec1 = { 1, 2, 3, 4, 5, 6 }; std::vector vec2 = vec1, vec3 = vec1; for (int& e: vec1 | std::views::filter(is_odd) | std::views::reverse) { e += 10; // OK: 条件is_odd(e)は維持される std::cout << e << ' '; } // 15 13 11 を出力 for (int& e: vec2 | std::views::filter(is_odd) | std::views::reverse) { e += 1; // NG: 条件is_odd(e)を満たさないためUB std::cout << e << ' '; } // GCC/Clang: SEGV発生 // 上記例から reverse, filter 適用順を入れ替え for (int& e: vec3 | std::views::reverse | std::views::filter(is_odd)) { e += 1; // NG: 本来はUBだが... std::cout << e << ' '; } // GCC/Clang: 6 4 2 を出力
C++20 24.7.4.3/p1より引用。
Modification of the element a
filter_view::iteratordenotes is permitted, but results in undefined behavior if the resulting value does not satisfy the filter predicate.
関連URL