yohhoyの日記

技術的メモをしていきたい日記

std::views::filter適用後の値書換えには要注意

C++標準ライブラリ提供レンジアダプタstd::views::filter適用後の要素に対する変更操作には十分留意すること。

変更操作により要素がフィルタ条件を満たさなくなる場合、C++ライブラリ仕様上は未定義動作(undefined behavior)を引き起こす。この問題は遅延評価によりフィルタ条件が複数評価されるケースで初めて表面化するため、ライブラリ仕様違反が潜在化するリスクが高い。C++ Ranges難しい(´・ω・)(・ω・`)ネー

#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::iterator denotes is permitted, but results in undefined behavior if the resulting value does not satisfy the filter predicate.

関連URL