最終更新日時: 2025年07月12日 02時16分44秒 (JST)
Koichi Murase が更新

履歴 編集

concept
<iterator>

# std::sentinel_for(C++20)

    namespace std {
      template<class S, class I>
      concept sentinel_for =
        semiregular<S> &&
        input_or_output_iterator<I> &&
        weakly-equality-comparable-with<S, I>;
    }

## 概要

`sentinel_for`は、任意の`semiregular`な型`S`がイテレータ型`I`の番兵（sentinel）型である事を表すコンセプトである。

番兵型とは、イテレータ範囲`[begin, end)`においての`end`を表す型の事である。これは通常`begin`の型と同じになるが、等値比較可能でありさえすれば別の型であっても構わない。

## モデル

型`I, S`の値`i, s`とそれによって示される範囲`[i, s)`について次の条件を満たす場合に限って、型`I, S`は`sentinel_for`のモデルである。

- `i == s`が適格である（未定義動作にならない）
- `bool(i != s) == true`の（`i`が範囲終端に到達していない）時、`i`は間接参照可能であり`[++i, s)`も範囲を示す
- `I&, S`が`assignable_from<I&, S>`のモデルとならないなら、構文的にも`assignable_from`コンセプトを満たさない

ここでの`==`の定義域は静的ではなく、実行時に変化しうる。`[i, s)`が範囲を示している時に`i == oi`となるような別のイテレータ`oi`をインクリメント（`++oi`）した後で、範囲`[i, s)`が有効であり続ける必要はない。

## 例

    #include <iostream>
    #include <iterator>
    #include <vector>

    template<typename I, std::sentinel_for<I> S>
    void f(const char* namei, const char* names) {
      std::cout << names << " is sentinel for " << namei << std::endl;
    }

    template<typename I, typename S>
    void f(const char* namei, const char* names) {
      std::cout << names << " is not sentinel for " << namei << std::endl;
    }

    struct sample_sentinel{};

    struct sample_input_or_output_iterator {
      friend auto operator++(sample_input_or_output_iterator&) -> sample_input_or_output_iterator&;
      friend auto operator++(sample_input_or_output_iterator&, int) -> sample_input_or_output_iterator;

      friend auto operator*(sample_input_or_output_iterator&) -> int;

      friend bool operator==(const sample_input_or_output_iterator&, sample_sentinel);

      using difference_type = int;
    };

    int main() {
      f<int*, int*>("int*", "int*");
      f<const int*, int*>("const int*", "int*");
      f<int*, const int*>("int*", "int* const");
      f<std::vector<int>::iterator, std::vector<int>::iterator>("std::vector<int>::iterator", "std::vector<int>::iterator");
      f<sample_input_or_output_iterator, sample_sentinel>("sample_input_or_output_iterator", "sample_sentinel");

      std::cout << "\n";
      f<std::vector<int>::iterator, int*>("std::vector<int>::iterator", "int*");
      f<double*, int*>("double*", "int*");
    }

### 出力

    int* is sentinel for int*
    int* is sentinel for const int*
    int* const is sentinel for int*
    std::vector<int>::iterator is sentinel for std::vector<int>::iterator
    sample_sentinel is sentinel for sample_input_or_output_iterator

    int* is not sentinel for std::vector<int>::iterator
    int* is not sentinel for double*

## バージョン

### 言語

- C++20

### 処理系

- Clang: ??
- GCC: 10.1 ✅
- Visual C++: 2019 Update 6 ✅

## 関連項目

- C++20 コンセプト

## 参照

- P0896R4 The One Ranges Proposal (was Merging the Ranges TS)
- LWG Issue 3453. Generic code cannot call `ranges::advance(i, s)`