C++20コンセプト時代のエラーメッセージとの付き合い方

# はじめに
C++プログラミングにコンパイルエラーは付き物です。コンパイラからのメッセージに頼らずに、正しいC++プログラムを書き上げることなど不可能です。（ここでは脳内C++コンパイラ搭載型超人の存在は無視します:D）

C++標準ライブラリは便利なアルゴリズムを数多く提供しており（通称 "STL"）、大部分のC++プログラマはその恩恵を受けています。しかしSTLを駆使したコードのコンパイルエラーは、時として長大で難解なメッセージを出力することがあります。

この記事ではソートアルゴリズム`sort`を題材にして、現状(C++17)エラーメッセージの問題点を確認し、きたるC++20時代のエラーメッセージを読み解いていきます。


# C++17時代のエラーメッセージ
下記ソースコード片をコンパイルすると、どんなエラーメッセージが出力されるでしょう？
注：このコードはコンパイルに失敗します。

```c++:cpp17sort_bad.cpp
#include <algorithm>
#include <list>

int main()
{
  // 双方向リンクリストの要素を並び替える
  std::list<int> lst = {5, 3, 1, 4, 2};
  std::sort(lst.begin(), lst.end());
  // ...
}
```
動作デモ: https://wandbox.org/permlink/4j5LtFOz5IMZjd0C

GCCコンパイラ(g++)が出した答えは...（可読性のため大幅に加工・省略）

```text:C++17エラーメッセージ
error: no match for 'operator-' (operand types are 'std::list::iterator<int>' and 'std::list::iterator<int>')
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr decltype ((__y.base() - __x.base())) std::operator-(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_IteratorR>&)'
  501 |     operator-(const reverse_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
note:   template argument deduction/substitution failed:
note:   'std::_List_iterator<int>' is not derived from 'const std::reverse_iterator<_Iterator>'
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr decltype ((__x.base() - __y.base())) std::operator-(const std::move_iterator<_IteratorL>&, const std::move_iterator<_IteratorR>&)'
 1542 |     operator-(const move_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
note:   template argument deduction/substitution failed:
note:   'std::list::iterator<int>' is not derived from 'const std::move_iterator<_IteratorL>'
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
```
<details><summary>（無加工のエラーメッセージ全体）</summary><div>

```
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:62,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algo.h: In instantiation of 'void std::__sort(_RandomAccessIterator, _RandomAccessIterator, _Compare) [with _RandomAccessIterator = std::_List_iterator<int>; _Compare = __gnu_cxx::__ops::_Iter_less_iter]':
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algo.h:4837:18:   required from 'void std::sort(_RAIter, _RAIter) [with _RAIter = std::_List_iterator<int>]'
prog.cc:8:35:   required from here
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algo.h:1955:22: error: no match for 'operator-' (operand types are 'std::_List_iterator<int>' and 'std::_List_iterator<int>')
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algobase.h:67,
                 from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:61,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_iterator.h:501:5: note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr decltype ((__y.base() - __x.base())) std::operator-(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_IteratorR>&)'
  501 |     operator-(const reverse_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_iterator.h:501:5: note:   template argument deduction/substitution failed:
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:62,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algo.h:1955:22: note:   'std::_List_iterator<int>' is not derived from 'const std::reverse_iterator<_Iterator>'
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algobase.h:67,
                 from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:61,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_iterator.h:1542:5: note: candidate: 'template<class _IteratorL, class _IteratorR> constexpr decltype ((__x.base() - __y.base())) std::operator-(const std::move_iterator<_IteratorL>&, const std::move_iterator<_IteratorR>&)'
 1542 |     operator-(const move_iterator<_IteratorL>& __x,
      |     ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_iterator.h:1542:5: note:   template argument deduction/substitution failed:
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:62,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_algo.h:1955:22: note:   'std::_List_iterator<int>' is not derived from 'const std::move_iterator<_IteratorL>'
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
```
</div></details>

このメッセージからエラー原因を読み取れるプログラマは、<del>相当にC++コンパイラに飼いならされた</del> もとい 相応の熟練者と言っても過言ではないでしょう。

本当の原因は「`std::list`は汎用の並び替えアルゴリズム`std::sort`に対応しない」ことです。より正確に表現するならば「`std::sort`はランダムアクセス・イテレータを要求する一方で、[`std::list`のイテレータはランダムアクセス可能ではない](https://timsong-cpp.github.io/cppwp/n4659/list#overview-1)」ためです。 [C++17標準ライブラリ`std::sort`](https://timsong-cpp.github.io/cppwp/n4659/sort) 定義では、[ランダムアクセス・イテレータを要求](https://timsong-cpp.github.io/cppwp/n4659/algorithms.requirements#3.5)する旨を間接的に表現しています。

```c++:std&#58;&#58;sort定義
template<class RandomAccessIterator>
  void sort(RandomAccessIterator first, RandomAccessIterator last);
// パラメータ名がRandomAccessIteratorの場合、ランダムアクセス・イテレータ要件を満たすべき
```


## おまけ：修正後コード
本記事はエラーメッセージ解釈のみを取り上げますが、さすがに修正後コードの掲示はしておきます。[`std::list`の`sort`メンバ関数](https://cpprefjp.github.io/reference/list/list/sort.html)を使う必要があります。

```c++:cpp17sort_fixed.cpp
// 双方向リンクリストの要素を並び替える
std::list<int> lst = {5, 3, 1, 4, 2};
lst.sort();
```


# C++20時代のエラーメッセージ：理想編
前掲エラーメッセージはイテレータに関して色々と指摘していますが、ランダムアクセス・イテレータへの言及はどこにも見当たりません。C++ STL利用の難しさの一因は、適切なエラーメッセージを提示できないことにもありそうです。

```text:C++17エラーメッセージ(一部)
error: no match for 'operator-' (operand types are 'std::list::iterator<int>' and 'std::list::iterator<int>')
 1955 |     std::__lg(__last - __first) * 2,
      |               ~~~~~~~^~~~~~~~~
```

哀れな迷えるC++プログラマを救うため、C++20では念願の [コンセプト(Concepts)](https://cpprefjp.github.io/lang/cpp20/concepts.html) が導入されました。コンセプト機能の提案文書 [N3580](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3580.pdf) でも「**テンプレート関連のエラーメッセージをより分かり易くする**[^n3580]」を掲げています。同文書より、期待されるメッセージ例を引用します

[^n3580]: "fundamentally improve diagnostics by checking template arguments in terms of stated intent at the point of use"

```text:N3580より引用
error: no matching function for call to 'sort(list<int>&)'
   sort(l);
        ^
note: candidate is:
note: template<Sortable T> void sort(T)
   void sort(T t) { }
        ^
note: template constraints not satisfied because
note: 'T' is not a/an 'Sortable' type [with T = list<int>] since
note: 'declval<T>()[n]' is not valid syntax
```

？？？：[「きた！コンセプトきた！」「C++コンセプトきた！」「これで勝つる！」](https://dic.nicovideo.jp/a/%E3%81%93%E3%82%8C%E3%81%A7%E5%8B%9D%E3%81%A4%E3%82%8B%21)

さらにC++20では、STLにも大きな機能追加が行われます。並び替えアルゴリズムの **従来版`std::sort`はそのまま** に、レンジライブラリ(Ranges Library)として  **`std::ranges::sort`が新たに追加** されます。早速、新しいレンジ版アルゴリズムを使ってみましょう。（参考記事「[C++ STLアルゴリズムの進化：並列化からレンジ拡張まで](https://qiita.com/yohhoy/items/8e741cb043143af49a62)」）

記述が煩雑なイテレータ・ペア`lst.begin(), lst.end()`ではなく、レンジ版には操作対象コンテナ`lst`をそのまま渡せます。すごく読み易い！ただしコンパイルは通らない。

```c++:cpp20sort.cpp
#include <algorithm>
#include <list>

int main()
{
  // 双方向リンクリストの要素を並び替える
  std::list<int> lst = {5, 3, 1, 4, 2};
  std::ranges::sort(lst);
  // ...
}
```
動作デモ: https://wandbox.org/permlink/FQpZBQalb2C3aUfp

ではC++コンパイラに叱られてみます（可読性のため大幅に加工・省略）

```text:C++20エラーメッセージ
error: no match for call to '(const std::ranges::__sort_fn) (std::list<int>&)'
    8 |   std::ranges::sort(lst);
      |                        ^
note: candidate: 'template<class I, class S, class Comp, class Proj>  
                    requires (random_access_iterator<I>) && (sentinel_for<S, I>) && (sortable<I, Comp, Proj>)
                  constexpr I std::ranges::__sort_fn::operator()(I, S, Comp, Proj) const'
 2018 |       operator()(I __first, S __last,
      |       ^~~~~~~~
note:   template argument deduction/substitution failed:
note:   candidate expects 4 arguments, 1 provided
    8 |   std::ranges::sort(lst);
      |                        ^
note: candidate: 'constexpr std::ranges::borrowed_iterator_t<R> std::ranges::__sort_fn::operator()(R&&, Comp, Proj) const
                  [with R = std::list<int>&; Comp = std::ranges::less; Proj = std::identity; std::ranges::borrowed_iterator_t<R> = std::list<int>::iterator]'
 2031 |       operator()(R&& __r, Comp __comp = {}, Proj __proj = {}) const
      |       ^~~~~~~~
note: constraints not satisfied
In instantiation of 'constexpr std::ranges::borrowed_iterator_t<R> std::ranges::__sort_fn::operator()(R&&, Comp, Proj) const
                        [with R = std::list<int>&; Comp = std::ranges::less; Proj = std::identity; std::ranges::borrowed_iterator_t<R> = std::list<int>::iterator]':
   required from here
   required for the satisfaction of 'derived_from<typename std::__detail::__iter_concept_impl<_Iter>::type, std::random_access_iterator_tag>' 
                                    [with _Iter = std::list<int>::iterator]
   required for the satisfaction of 'random_access_iterator<decltype (std::__detail::__ranges_begin(declval<_Container&>()))>'
                                    [with _Container = std::list<int>&]
   required for the satisfaction of 'random_access_range<R>'
                                    [with R = std::list<int>&]
 note:   'std::random_access_iterator_tag' is not a base of 'std::bidirectional_iterator_tag'
   67 |     concept derived_from = __is_base_of(_Base, _Derived)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

<details><summary>（無加工のエラーメッセージ全体）</summary><div>

```
prog.cc: In function 'int main()':
prog.cc:8:24: error: no match for call to '(const std::ranges::__sort_fn) (std::__cxx11::list<int>&)'
    8 |   std::ranges::sort(lst);
      |                        ^
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:64,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/ranges_algo.h:2018:7: note: candidate: 'template<class _Iter, class _Sent, class _Comp, class _Proj>  requires (random_access_iterator<_Iter>) && (sentinel_for<_Sent, _Iter>) && (sortable<_Iter, _Comp, _Proj>) constexpr _Iter std::ranges::__sort_fn::operator()(_Iter, _Sent, _Comp, _Proj) const'
 2018 |       operator()(_Iter __first, _Sent __last,
      |       ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/ranges_algo.h:2018:7: note:   template argument deduction/substitution failed:
prog.cc:8:24: note:   candidate expects 4 arguments, 1 provided
    8 |   std::ranges::sort(lst);
      |                        ^
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:64,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/ranges_algo.h:2031:7: note: candidate: 'constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__sort_fn::operator()(_Range&&, _Comp, _Proj) const [with _Range = std::__cxx11::list<int>&; _Comp = std::ranges::less; _Proj = std::identity; std::ranges::borrowed_iterator_t<_Range> = std::conditional<true, std::_List_iterator<int>, std::ranges::dangling>::type]'
 2031 |       operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const
      |       ^~~~~~~~
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/ranges_algo.h:2031:7: note: constraints not satisfied
In file included from /opt/wandbox/gcc-head/include/c++/11.0.0/compare:39,
                 from /opt/wandbox/gcc-head/include/c++/11.0.0/bits/stl_pair.h:65,
                 from /opt/wandbox/gcc-head/include/c++/11.0.0/utility:70,
                 from /opt/wandbox/gcc-head/include/c++/11.0.0/algorithm:60,
                 from prog.cc:1:
/opt/wandbox/gcc-head/include/c++/11.0.0/concepts: In instantiation of 'constexpr std::ranges::borrowed_iterator_t<_Range> std::ranges::__sort_fn::operator()(_Range&&, _Comp, _Proj) const [with _Range = std::__cxx11::list<int>&; _Comp = std::ranges::less; _Proj = std::identity; std::ranges::borrowed_iterator_t<_Range> = std::conditional<true, std::_List_iterator<int>, std::ranges::dangling>::type]':
prog.cc:8:24:   required from here
/opt/wandbox/gcc-head/include/c++/11.0.0/concepts:67:13:   required for the satisfaction of 'derived_from<typename std::__detail::__iter_concept_impl<_Iter>::type, std::random_access_iterator_tag>' [with _Iter = std::_List_iterator<int>]
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/iterator_concepts.h:599:13:   required for the satisfaction of 'random_access_iterator<decltype (std::__detail::__ranges_begin(declval<_Container&>()))>' [with _Container = std::__cxx11::list<int, std::allocator<int> >&]
/opt/wandbox/gcc-head/include/c++/11.0.0/bits/range_access.h:923:13:   required for the satisfaction of 'random_access_range<_Range>' [with _Range = std::__cxx11::list<int, std::allocator<int> >&]
/opt/wandbox/gcc-head/include/c++/11.0.0/concepts:67:28: note:   'std::random_access_iterator_tag' is not a base of 'std::bidirectional_iterator_tag'
   67 |     concept derived_from = __is_base_of(_Base, _Derived)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
</div></details>

なんて簡潔で明解なエラーメッセージ！ ...ではないですね。目を凝らせばランダムアクセス・イテレータへの言及も見つかりはしますが、多くのC++プログラマが期待するメッセージからは程遠いと思われます。

？？？：「嘘だ‥‥‥夢だろ‥‥これ‥‥夢に決まってる‥‥‥‥‥！」
？？？：[「ところがどっこい‥‥‥‥夢じゃありません‥‥‥‥！ 現実です‥‥‥！ これが現実‥！」](https://dic.nicovideo.jp/a/%E3%81%A8%E3%81%93%E3%82%8D%E3%81%8C%E3%81%A9%E3%81%A3%E3%81%93%E3%81%84........%E5%A4%A2%E3%81%98%E3%82%83%E3%81%82%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93........%21)


# C++20時代のエラーメッセージ：実践編
さて、ここからが本題です。C++20時代のエラーメッセージを順番に解釈していきましょう。
エラー(error)としては「`std::list<int>&`を引数に1つだけ取る`std::ranges::sort`が見つからない[^prevent-adl]」とのことです。あれ？

[^prevent-adl]: エラーメッセージを注意深く読むと`std::ranges::sort`関数テンプレートではなく、`std::ranges::__sort_fn`関数オブジェクトに対する`operator()`演算子オーバーロード呼び出しとなっています。これは[レンジライブラリのADL要件](https://timsong-cpp.github.io/cppwp/n4861/algorithms.requirements#2)を満たす措置となっています。

```text:C++20エラーメッセージ(一部)
error: no match for call to '(const std::ranges::__sort_fn) (std::list<int>&)'
    8 |   std::ranges::sort(lst);
      |                        ^
```

まずは [C++20標準ライブラリ`std::ranges::sort`](https://timsong-cpp.github.io/cppwp/n4861/sort) 定義を確認しましょう。どうやら、2種類のオーバーロードが提供されるようです。

```C++:std&#58;&#58;ranges&#58;&#58;sort定義
// オーバーロード#1
template<random_­access_­iterator I, sentinel_­for<I> S, class Comp = ranges::less, class Proj = identity>
  requires sortable<I, Comp, Proj>
constexpr I
  ranges::sort(I first, S last, Comp comp = {}, Proj proj = {});

// オーバーロード#2
template<random_­access_­range R, class Comp = ranges::less, class Proj = identity>
  requires sortable<iterator_t<R>, Comp, Proj>
constexpr borrowed_iterator_t<R>
  ranges::sort(R&& r, Comp comp = {}, Proj proj = {});
```

最新のC++コンセプト機能がふんだんに使われており、ちょっと難易度が高いかもしれません。説明のために、C++17知識でも読める形に簡約しておきます。（今回無関係な`constexpr`省略、戻り値型は等価表現へ置換[^borrowed_iterator]）

[^borrowed_iterator]: `borrowed_iterator_t<std::list<int>&>`は`std::list<int>::iterator`

```C++:std&#58;&#58;ranges&#58;&#58;sort定義(簡約版)
// オーバーロード#1
template<class I, class  S, class Comp = ranges::less, class Proj = identity>
I ranges::sort(I first, S last, Comp comp = {}, Proj proj = {});

// オーバーロード#2
template<class R, class Comp = ranges::less, class Proj = identity>
I ranges::sort(R&& r, Comp comp = {}, Proj proj = {});
  // I==レンジRのイテレータ型
```

イテレータのペア(`first, last`)を取る4引数版オーバーロード#1と、レンジ(`r`)を取る3引数版オーバーロード#2が存在するようです。いずれのオーバーロードでも、`comp`, `proj`は省略可能とのこと。

```text:C++20エラーメッセージ(一部)
note: candidate: 'template<class I, class S, class Comp, class Proj>  
                    requires (random_access_iterator<I>) && (sentinel_for<S, I>) && (sortable<I, Comp, Proj>)
                  constexpr I std::ranges::__sort_fn::operator()(I, S, Comp, Proj) const'
 2018 |       operator()(I __first, S __last,
      |       ^~~~~~~~
note:   template argument deduction/substitution failed:
note:   candidate expects 4 arguments, 1 provided
    8 |   std::ranges::sort(lst);
      |                        ^
```

上記noteは「イテレータ・ペアをとるオーバーロード#1にはマッチしなかった」と言っています。今回はイテレータ・ペアを使っていませんから、ごもっともな指摘ですね。

```text:C++20エラーメッセージ(一部)
note: candidate: 'constexpr std::ranges::borrowed_iterator_t<R> std::ranges::__sort_fn::operator()(R&&, Comp, Proj) const
                  [with R = std::list<int>&; Comp = std::ranges::less; Proj = std::identity; std::ranges::borrowed_iterator_t<R> = std::list<int>::iterator]'
 2031 |       operator()(R&& __r, Comp __comp = {}, Proj __proj = {}) const
      |       ^~~~~~~~
note: constraints not satisfied
```

もう一方のnoteは「レンジをとるオーバーロード#2では、**制約(constraints)を満たせなかった(not satisfied)**」と言っています。なるほど？

C++コンセプトは、テンプレートパラメータが満たすべき制約条件を表現する機能です。つまり`std::list<int>`型ではアルゴリズム`std::ranges::sort`が要求する制約を満たせないと指摘しています。レンジを引数に取るオーバーロード#2の定義を再確認しましょう。

```C++:std&#58;&#58;ranges&#58;&#58;sort定義(オーバーロード#2)
template<random_­access_­range R, class Comp = ranges::less, class Proj = identity>
  requires sortable<iterator_t<R>, Comp, Proj>
constexpr borrowed_iterator_t<R>
  ranges::sort(R&& r, Comp comp = {}, Proj proj = {});
```

ちょっとばかり複雑ですね。ここでは説明に直接関わる部分だけを残して、読みやすさのため思い切り変形してしまいます[^template-concept]。`requires`節で記述される式が、テンプレートパラメータに対する制約を表します。

[^template-concept]: `template<random_­access_­range R> requires C` は `template<class R> requires (random_­access_­range<R> && C)` と等価。

```C++:std&#58;&#58;ranges&#58;&#58;sort定義(オーバーロード#2変形)
template<class R, class Comp = /*...*/, class Proj = /*...*/>
  requires (random_­access_­range<R> && sortable</*...*/>)
I ranges::sort(R&& r, Comp comp = {}, Proj proj = {});
  // I==レンジRのイテレータ型
```

`requires`節にある1番目の制約式`random_­access_­range<R>`は、レンジ型(`R`)ではランダムアクセス可能であるべきという制約を表します。いよいよ真相に近づいてきました。

```text:C++20エラーメッセージ(一部)
In instantiation of 'constexpr std::ranges::borrowed_iterator_t<R> std::ranges::__sort_fn::operator()(R&&, Comp, Proj) const
                        [with R = std::list<int>&; Comp = std::ranges::less; Proj = std::identity; std::ranges::borrowed_iterator_t<R> = std::list<int>::iterator]':
   required from here
   required for the satisfaction of 'derived_from<typename std::__detail::__iter_concept_impl<_Iter>::type, std::random_access_iterator_tag>' 
                                    [with _Iter = std::list<int>::iterator]
   required for the satisfaction of 'random_access_iterator<decltype (std::__detail::__ranges_begin(declval<_Container&>()))>'
                                    [with _Container = std::list<int>&]
   required for the satisfaction of 'random_access_range<R>'
                                    [with R = std::list<int>&]
 note:   'std::random_access_iterator_tag' is not a base of 'std::bidirectional_iterator_tag'
   67 |     concept derived_from = __is_base_of(_Base, _Derived)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

ここまでの知識を踏まえて脳内フィルタを通すと「**required for the satisfaction of 'random_access_range&lt;R&gt;'**」が見つかるでしょうか。

Q.E.D.


## エクストラ・ステージ
やはり最終行のnoteは気になりますか？これは、おそらく、C++コンセプト仕様によるエラーメッセージ表現の限界と考えられます。

```text:C++20エラーメッセージ(一部)
 note:   'std::random_access_iterator_tag' is not a base of 'std::bidirectional_iterator_tag'
   67 |     concept derived_from = __is_base_of(_Base, _Derived)
      |                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```

[コンセプト`random_access_range`](https://timsong-cpp.github.io/cppwp/n4861/range.refinements#lib:random_access_range) はさらに別のコンセプトを使って定義されており、途中経緯を全てすっ飛ばすと [コンセプト`derived_from`](https://timsong-cpp.github.io/cppwp/n4861/concept.derived) による制約式が登場します。

```c++
derived_from<bidirectional_iterator_tag, random_access_iterator_tag>
```

C++標準規格とは異なる制約式になっていますが、GCCコンパイラは効率化のため [型特性コンパイラサポート機能`__is_base_of`](https://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html) を利用するようです。ここではエラー原因の最小単位となる原始制約式(atomic constraints)として、該当行が出力されたのだと推測されます。
（参考：[https://twitter.com/yohhoy/status/1278576616440860673](https://twitter.com/yohhoy/status/1278576616440860673)）