摘要訊息 : 千呼萬喚始出來的 Concept.

0. 前言

本文寫於 2019 年, 當時 C++ 20 標準並沒有發布, 所以本文可能和引入 C++ 20 的 Concept 存在一些差異. 本文存在極大的過時風險!

本文於 2022 年 4 月 20 日進行一次更新和修正. 修正之後本文已經歸檔, 不再享受更新.

1. 基本概念

在本節中, 我們將對 Concept 的基本概念進行介紹, 大家通過本節可以大致了解 Concept 並且學會一些初級用法.

1.1 關鍵字

Concept 引入了兩個的關鍵字 : conceptrequires. 這兩個關鍵字是 C++ 17 以及之前所不存在的.

我們把滿足某一類制約要求的型別集合稱為 Concept. 在 Concept 中, 這些型別的推算本質上還是超編程, 在編碼期進行運算.

1.2 定義

Concept 可以寫為

template <template-parameter-list>
concept concept-name = constraint-expression;

其中, template-parameter-list 是樣板參數列表, concept-name 是 Concept 的名稱, constraint-expression 是制約表達式. 制約表達式是限定型別集合的根本, 它制約了哪一些型別滿足要求, 哪些型別不滿足要求.

能夠使得 is_unsigned<T>::value 的值為 true 的 Concept 可以寫成

#include <type_traits>

template <typename T>
concept unsigned_type = std::is_unsigned_v<T>;

1.3 用法

一個 Concept 可以直接替代樣板參數中的 typename, 用於制約樣板引數 :

#include <type_traits>

template <typename T>
concept unsigned_type = std::is_unsigned_v<T>;

template <unsigned_type T>
void f(T a);

int main(int argc, char *argv[]) {
    f(0.0f);        // Error, float is not a unsigned type
}

除了這種寫法之外, 還可以使用 requires 表達式 :

#include <type_traits>

template <typename T>
concept unsigned_type = std::is_unsigned_v<T>;

template <typename T> requires unsigned_type<T>
void f1(T a);
template <typename T>
void f2(T a) requires unsigned_type<T>;
template <typename T> requires std::is_unsigned_v<T>
void f3(T a);
template <typename T>
void f4(T a) requires std::is_unsigned_v<T>;

上面四種宣告都是等價的. 不過需要注意的是 requires 放在函式參數列表之後的時候, 必須放在 {} 前面, 所有標識之後.

1.4 制約表達式

制約表達式是一個 bool 型別的常數表達式, 並且任何可以通過隱含型別轉換而轉為 bool 型別的轉型都將被拒絕, 即制約表達式產生的是一個嚴格的 bool 型別右值 :

struct to_bool {
    constexpr operator bool() const noexcept {
        return true;
    }
};

template <typename T> requires(T {})        // Error
void func(T) {}

int main(int argc, char *argv[]) {
    func(to_bool {});
}

1.4.1 結合制約表達式

結合制約表達式是要求樣板引數同時滿足多個要求, 例如要求樣板引數同時滿足遞增和遞減兩種運算 :

template <typename T> requires(incrementable<T> and decrementable<T>)
void func(const T &);

一旦結合制約表達式中某個不成立, 就會放棄後面的推斷.

1.4.2 析取制約表達式

析取制約表達式是要求樣板引數只要滿足部分要求即可, 例如要求樣板引數是一個無號數型別或者 void 型別 :

template <typename T> requires(unsigned_type<T> or void_type<T>)
T func();

一旦析取制約表達式中某一個成立, 也會放棄後面的推斷.

1.4.3 原子制約表達式

原子制約表達式中除了會包含結合制約表達式或者析取制約表達式之外, 還包含了布林常數表達式 :

template <typename T>
concept all_pass = true;
template <typename T>
concept all_block = false;

template <all_pass T>
void func(T) {}
template <all_block T>
void func2(T) {}

int main(int argc, char *argv[]) {
    func(0u);       // 可以放入任意型別
    func2(0);       // 放入任意型別都將被阻擋, 產生編碼錯誤
}

在原子制約表達式計算的過程中, 編碼器需要將整個表達式進行制約正規化. 所謂制約正規化, 就是把制約表達式中各個子表達式變換為結合和析取的過程. 在進行 Concept 的任何一步分析之前, 編碼器都會進行制約正規化. 這個過程中, 還會涉及到另一個概念, 就是參數影射. 參數影射是 Concepts 中的樣板參數被實際的樣板引數所取代並且進行計算的過程. 比如 :

template <typename T>
concept Foo = T::value or true;

對於 Foo<T> 中的 T 被實際的引數所替代, 這個過程就稱為參數影射. 這種現象中可能會出現一些無效的型別或者無效的表達式. 例如現在使用 int * 來替換 T, 我們都知道不可能存在 int *::value 這種值. 如果出現這種情況, 那麼程式就是病式的, 但是編碼器可以無需對其進行檢查. 由於這種屬於實作定義行為, 在 GCC 中, GCC 針對這種情況進行了檢查, 最終 GCC 會產生編碼錯誤. 制約表達式還可以針對摺疊表達式進行制約正規化, 但是具體是如何, 我目前還沒有研究出來. 因為我所寫的所有關於 Concept 和摺疊表達式的程式碼都沒有通過 GCC 編碼.

1.4.4 函式宣告

由於對於函式樣板參數的制約有三種寫法, 因此有時可能會導致重複 :

template <typename T>
concept reference_type = is_reference_v<T>;
template <typename T>
concept rvalue_reference_type = is_rvalue_reference_v<T>;

template <reference_type T> requires rvalue_reference_type<T>
void func(T);       // #1
template <reference_type T> requires rvalue_reference_type<T>
void func(T);       // #2
template <rvalue_reference_type T> requires reference_type<T>
void func(T);       // #3
template <typename T> requires reference_type<T> and rvalue_reference_type<T>
void func(T);       // #4

#1 是正常的宣告, 並且是第一次針對 func 函式的宣告; #2 也是屬於正常的宣告, 並且是再次針對 func 函式的宣告, 這也沒有問題; #3#4 本質上也是對 func 函式的宣告, 但是宣告的時候調換了制約的順序, 這將會導致程式碼是病式的. 編碼器並不一定會針對 #3#4 的宣告進行檢查, GCC 就沒有進行這樣的檢查.

1.5 requires 表達式

在制約表達式中, 我們已經展示了 requires 的其中一種用法, 這種用法稱為 requires 陳述式. requires 表達式還有另外一種用法.

我們有時候需要實作一些標準程式庫中沒有的樣板. 例如 boost 程式庫中有 boost::has_plus_operator 這樣的樣板, 而標準程式庫中沒有, 當沒有安裝 boost 程式庫的時候, 我們可能需要這樣去實作 :

#include <utility>
#include <iostream>

using namespace std;

template <typename, typename T>
struct select_second_type {
    using type = T;
};

template <typename T, typename U>
constexpr inline typename select_second_type<decltype(declval<T>() + declval<U>()), true_type>::type test_plus_operator(int) noexcept;
template <typename, typename>
constexpr inline false_type test_plus_operator(...) noexcept;

template <typename T, typename U = T>
struct has_plus_operator {
    constexpr static inline auto value {is_same_v<decltype(test_plus_operator<T, U>(0)), true_type>};
};

int main(int argc, char *argv[]) {
    cout << has_plus_operator<int, char>::value << endl;     //輸出結果 : 1
}

此時, 我們再運用 has_plus_operator 去實作一個 Concept 就顯得過於麻煩了. 因此, 為了簡化程式碼, 在 Concept 中還引入了 requires 表達式. requires 表達式的結果也是一個布林常數表達式, 它的語法是這樣的 :

template <template-parameter-list>
concept concept-name = requires(requires-parameter-list) {
    requires-sequences;
};

其中, (requires-parameter-list) 是指可選的 requires 表達式的參數列表, 這個參數列表和函式的參數列表相同, 但是 requires 表達式的參數列表中不允許存在預設引數和不允許省略號結尾 (省略號是指 C-Style 可變參數, 而非可變樣板參數). 這些參數沒有生命週期, 並且可視範圍僅限於 Concept 內. requires-sequences 用於描述制約的型別, 它有四種類型.

1.5.1 簡約 requires

簡約requires 是任意的表達式, 只要型別滿足表達式, 那麼需求表達式的結果為 true :

template <typename T, typename U>
concept addable = requires(T &t, U &u) {
    t + u;
    swap(t, u);
};

template <typename T, typename U> requires(addable<T, U>)
void func(T t, U u) {
    t + u;
    swap(t, u);
}

Concept addable 要求型別 T 的物件可以和型別 U 的物件相加並且可以使用函式 swap 交換.

簡約 requires 進行判斷的時候, 不會對表達式進行實際計算, 只會判斷給出的引數對應的表達式是否合法.

簡約 requires 的寫法不一定要像上述程式碼一樣, 它可以直接被放在函式樣板的樣板參數列表之後 :

template <typename T> requires(++(T {}))
void func(T);

1.5.2 型別 requires

型別 requires 用於檢查某個型別名稱是否合法, 但是型別必須被 typename 修飾, 用於告知編碼器這是一個型別 :

template <typename T>
concept nested_type_check = requires {
    typename T::type;
};

template <typename T>
concept template_specialization_check = requires {
    typename Foo<T>;
};

template_specialization_check 用於檢查 Foo<T> 型別是否合法.

1.5.3 複合 requires

符合 requires 類似於函式的宣告. 它表示了一個表達式的基本屬性, 包括是否會擲出例外情況, 能不能滿足制約, 複合 requires 的基本形式為 {expression} noexcept -> returning-type. 其中, {expression} 中的表達式實際上就是一個簡單 requires, 而 noexcept 表示表達式是否滿足不擲出例外情況的要求, 它是可選的; -> returning-type 同樣也是可選的, 它表示表達式最終的回傳型別應該是什麼型別或者應該滿足什麼制約.

#include <type_traits>

template <typename T>
concept unsigned_type = std::is_unsigned_v<T>;

template <typename T>
concept returning_type_check = requires(T &&a) {
    {++a} noexcept -> unsigned_type<T>;
};

上述程式碼暫時無法在 GCC 中通過, 我還要查閱一些資料才能確定它是否被納入了 C++ 20 標準.

1.5.4 巢狀 requires

requires 表達式中包含另外一個 requires 表達式, 稱為巢狀 requires 表達式 :

#include <type_traits>

template <typename T, typename U>
concept same_type = std::is_same_v<T, U> and std::is_same_v<U, T>;

template <typename T>
concept default_constructible = requires(T t, size_t size) {
    requires same_type<T *, decltype(new T[size])>;
    requires same_type<decltype(&t), T *>;
    T {};
    new T;
    new T [size];
};

2. Concept 與 SFINAE

我們首先介紹制約之間的涵括 (subsume, 也稱作歸入). 當制約 P 代表的型別集合中的任意型別都可以在制約 Q 所代表的型別集合中找到, 那麼稱制約 Q 涵括制約 P (制約 P 歸入制約 Q). 特別地, 當制約 Q 沒有任何限制時, 任意的制約 P 和制約 Q 沒有涵括或者歸入關係.

當 Concept 和 SFINAE 結合的時候, 被制約的函式樣板在匹配的過程中, 總是和滿足更多制約的那一個匹配 :

#include <iostream>

using namespace std;

template <typename T>
concept incrementable = requires(T t) {
    ++t;
};
template <typename T>
concept dereferenceable = requires(T t) {
    *t;
    requires incrementable<T>;
};

/* #1 */
template <typename T>
void func(T) {
    cout << "no constraints" << endl;
}
/* #2 */
template <incrementable T>
void func(T) {
    cout << "incrementable only" << endl;
}
/* #3 */
template <dereferenceable T>
void func(T) {
    cout << "incrementable and dereferenceable" << endl;
}

int main(int argc, char *argv[]) {
    struct Foo {};
    func(Foo {});       // 輸出 : no constraints, 匹配 #1
    func(0);        // 輸出 : incrementable only, 匹配 #2
    func((int *)0);     // 輸出 : incrementable and dereferenceable, 匹配 #3
}

如果兩個 Concepts 對應的型別集合相同, 也就是當存在制約 P 與制約 Q, 既有制約 P 涵括制約 Q, 同時也有制約 Q 涵括制約 P, 那麼制約 P 與制約 Q 是相等關係. 當函式多載中出現兩個制約相等, 那麼將會引起模稜兩可的呼叫, 導致編碼錯誤 :

template <typename T>
concept incrementable = requires(T t) {
    ++t;
};
template <typename T>
concept dereferenceable = requires(T t) {
    *t;
    requires incrementable<T>;
};
template <typename T>
concept incrementable_and_dereferenceable = requires(T t) {
    ++t;
    *t;
};

template <dereferenceable T>
void func(T) {
    cout << "incrementable and dereferenceable" << endl;
}
template <incrementable_and_dereferenceable T>
void func(T) {}

int main(int argc, char *argv[]) {
    func((int *)0);     // Error : call of overloaded 'func(int*)' is ambiguous
}

3. 評論

樣板超編程一直都是 C++ 比較難的地方, 也只有少數的 C++ 程式設計師才會去學習, 通常這些 C++ 程式設計師都是通用程式庫的作者. 他們為了追求極致的性能放棄了程式碼的易讀性. 在 C++ 2a 之前, 所有樣板超編程的程式碼都非常難讀懂. 我們要實作某個特性的時候, 其它程式設計語言可能只需要幾行簡單的程式碼, 但是 C++ 要通過樣板超編程實作並且可能需要幾十行. C++ 引入 Concept 之後, 編碼期計算的程式碼變得非常容易寫, 甚至有時候我們只需要用常規的思想就可以寫出來. 對於程式碼的閱讀者來說, 這更是比樣板超編程要清晰得多.

當然, 除了易寫和易讀這兩個特點之外, Concept 可以簡化編碼錯誤提示.

#include <forward_list>
#include <algorithm>

using namespace std;
int main(int argc, char *argv[]) {
    forward_list<int> f;
    sort(f.begin(), f.end());
}

由於標準程式庫的 std::sort 函式要求函式引數必須是兩個隨機訪問疊代器, 而 std::forward_list 的疊代器是前向疊代器, 因此上面程式碼是錯誤的. 理想的編碼錯誤提示應該是這樣的 :

main.cpp:7:5 : error : cannot call std::sort with the unsatisfied type forward_list<int>::iterator (from f.begin()) and forward_list<int>::iterator (from f.end())
function std::sort note : the arguments of std::sort should satisfy random access iterator

然而, 邪惡的 Clang++ 是這麼提示你 :

/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3924:40: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>')
difference_type __len = __last - __first;
~~~~~~ ^ ~~~~~~~
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4117:5: note: in instantiation of function template specialization 'std::__1::__sort<std::__1::__less<int, int> &, std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *> >' requested here
__sort<_Comp_ref>(__first, __last, __comp);
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4126:12: note: in instantiation of function template specialization 'std::__1::sort<std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>, std::__1::__less<int, int> >' requested here
_VSTD::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
^
/Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:8:5: note: in instantiation of function template specialization 'std::__1::sort<std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *> >' requested here
sort(f.begin(), f.end());
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:743:1: note: candidate template ignored: could not match 'reverse_iterator' against '__forward_list_iterator'
operator-(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1184:1: note: candidate template ignored: could not match 'move_iterator' against '__forward_list_iterator'
operator-(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1592:1: note: candidate template ignored: could not match '__wrap_iter' against '__forward_list_iterator'
operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT_DEBUG
^
In file included from /Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:179:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3952:9: error: cannot decrement value of type 'std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>'
--__lm1;
^ ~~~~~
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3984:28: error: cannot decrement value of type 'std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>'
if (__i == --__j)
^ ~~~
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4039:17: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>')
if (__i < __j)
~~~ ^ ~~~
/Library/Developer/CommandLineTools/usr/include/c++/v1/utility:594:1: note: candidate template ignored: could not match 'pair' against '__forward_list_iterator'
operator< (const pair<_T1,_T2>& __x, const pair<_T1,_T2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:702:1: note: candidate template ignored: could not match 'reverse_iterator' against '__forward_list_iterator'
operator<(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1143:1: note: candidate template ignored: could not match 'move_iterator' against '__forward_list_iterator'
operator<(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1515:1: note: candidate template ignored: could not match '__wrap_iter' against '__forward_list_iterator'
operator<(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT_DEBUG
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/tuple:1182:1: note: candidate template ignored: could not match 'tuple' against '__forward_list_iterator'
operator<(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/memory:2970:1: note: candidate template ignored: could not match 'unique_ptr' against '__forward_list_iterator'
operator< (const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/memory:3028:1: note: candidate template ignored: could not match 'unique_ptr' against '__forward_list_iterator'
operator<(const unique_ptr<_T1, _D1>& __x, nullptr_t)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/memory:3037:1: note: candidate template ignored: could not match 'unique_ptr' against '__forward_list_iterator'
operator<(nullptr_t, const unique_ptr<_T1, _D1>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/memory:4808:1: note: candidate template ignored: could not match 'shared_ptr' against '__forward_list_iterator'
operator<(const shared_ptr<_Tp>& __x, const shared_ptr<_Up>& __y) _NOEXCEPT
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/memory:4878:1: note: candidate template ignored: could not match 'shared_ptr' against '__forward_list_iterator'
operator<(const shared_ptr<_Tp>& __x, nullptr_t) _NOEXCEPT
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/memory:4886:1: note: candidate template ignored: could not match 'shared_ptr' against '__forward_list_iterator'
operator<(nullptr_t, const shared_ptr<_Tp>& __x) _NOEXCEPT
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:1706:6: note: candidate template ignored: could not match 'forward_list' against '__forward_list_iterator'
bool operator< (const forward_list<_Tp, _Alloc>& __x,
^
In file included from /Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:179:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4073:65: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'int')
if (_VSTD::__insertion_sort_incomplete<_Compare>(__i+1, __last, __comp))
~~~^~
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:761:1: note: candidate template ignored: could not match 'reverse_iterator<type-parameter-0-0>' against 'int'
operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1202:1: note: candidate template ignored: could not match 'move_iterator<type-parameter-0-0>' against 'int'
operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1618:1: note: candidate template ignored: could not match '__wrap_iter<type-parameter-0-0>' against 'int'
operator+(typename __wrap_iter<_Iter>::difference_type __n,
^
In file included from /Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:179:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4090:17: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>')
if (__i - __first < __last - __i)
~~~ ^ ~~~~~~~
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:743:1: note: candidate template ignored: could not match 'reverse_iterator' against '__forward_list_iterator'
operator-(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1184:1: note: candidate template ignored: could not match 'move_iterator' against '__forward_list_iterator'
operator-(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1592:1: note: candidate template ignored: could not match '__wrap_iter' against '__forward_list_iterator'
operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT_DEBUG
^
In file included from /Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:179:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3832:20: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>')
switch (__last - __first)
~~~~~~ ^ ~~~~~~~
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4072:32: note: in instantiation of function template specialization 'std::__1::__insertion_sort_incomplete<std::__1::__less<int, int> &, std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *> >' requested here
bool __fs = _VSTD::__insertion_sort_incomplete<_Compare>(__first, __i, __comp);
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4117:5: note: in instantiation of function template specialization 'std::__1::__sort<std::__1::__less<int, int> &, std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *> >' requested here
__sort<_Comp_ref>(__first, __last, __comp);
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:4126:12: note: in instantiation of function template specialization 'std::__1::sort<std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>, std::__1::__less<int, int> >' requested here
_VSTD::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
^
/Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:8:5: note: in instantiation of function template specialization 'std::__1::sort<std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *> >' requested here
sort(f.begin(), f.end());
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:743:1: note: candidate template ignored: could not match 'reverse_iterator' against '__forward_list_iterator'
operator-(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1184:1: note: candidate template ignored: could not match 'move_iterator' against '__forward_list_iterator'
operator-(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1592:1: note: candidate template ignored: could not match '__wrap_iter' against '__forward_list_iterator'
operator-(const __wrap_iter<_Iter1>& __x, const __wrap_iter<_Iter2>& __y) _NOEXCEPT_DEBUG
^
In file included from /Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:179:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3852:40: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'int')
_RandomAccessIterator __j = __first+2;
~~~~~~~^~
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:761:1: note: candidate template ignored: could not match 'reverse_iterator<type-parameter-0-0>' against 'int'
operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1202:1: note: candidate template ignored: could not match 'move_iterator<type-parameter-0-0>' against 'int'
operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1618:1: note: candidate template ignored: could not match '__wrap_iter<type-parameter-0-0>' against 'int'
operator+(typename __wrap_iter<_Iter>::difference_type __n,
^
In file included from /Users/jonny/Library/Mobile Documents/com~apple~CloudDocs/Xcode/C++/CLionDebug/main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/forward_list:179:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:3853:39: error: invalid operands to binary expression ('std::__1::__forward_list_iterator<std::__1::__forward_list_node<int, void *> *>' and 'int')
__sort3<_Compare>(__first, __first+1, __j, __comp);
~~~~~~~^~
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:761:1: note: candidate template ignored: could not match 'reverse_iterator<type-parameter-0-0>' against 'int'
operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1202:1: note: candidate template ignored: could not match 'move_iterator<type-parameter-0-0>' against 'int'
operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x)
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/iterator:1618:1: note: candidate template ignored: could not match '__wrap_iter<type-parameter-0-0>' against 'int'
operator+(typename __wrap_iter<_Iter>::difference_type __n,
^
9 errors generated.

...

......

.........

沒錯, 這個編碼錯誤的提示非常刺激, C++ 的老手們也需都要斟酌一段時間. 而 C++ 2a 引入 Concept 之後, 錯誤提示就會非常清楚.