Tip : 本文正在不斷修正中, 對於可能出現的錯誤, 最終以 C++ 20 標準為準

如果你曾閱讀過《Template Meta Programming》系列的文章或者閱讀過 C++ 標準程式庫的程式碼, 那麼有關超編程的程式碼一定讓你非常頭疼. 特別是對於剛剛入門超編程的人來說, 儘管你可能並不是讀不懂程式碼, 不過難看的超編程程式碼直接會讓你感覺恐懼

Concept 這個概念早就被人提出了, 只不過 C++ 20 才準備將其標準化, 它就是為了減少超編程程式碼而生的. 本篇將對 Concept 進行介紹

關鍵字 : concept, requires

一個樣板可能會對樣板參數具有一定的限制, 這導致只有指定的樣板引數才會使樣板具現化, 否則會引起編碼錯誤 :

#include <iostream>



using namespace std;



template <typename T>

void func(T, enable_if_t<is_unsigned<T>::value, T>) {}



int main(int argc, char *argv[]) {

    func(0u, 0u);       //OK

    func(0, -2);        //Error : candidate template ignored: requirement 'is_unsigned::value' was not satisfied [with T = int]

}

為了避免上面這樣麻煩的程式碼, 也為了程式碼的多用, 同時為了清楚編碼錯誤的提示, C++ 引入了 Concept. 像上面這樣滿足 is_unsigned<T> 的所有型別集合, 也就是滿足對於樣板參數的約束的型別集合稱為 Concepts

在 Concept 中, 這些型別的推算本質上還是超編程, 在編碼期進行運算

1. Concept 基礎

template <template-parameter-list>

concept concept-name = constraint-expression;

Concept 就像一個物件一樣, 必須以分號結尾, 但是它只存在於編碼期而且它具有作用範圍. 其中的 constraint-expression, 我暫稱之為制約表達式, 這是限定樣板引數的主要部分

那麼滿足 is_unsigned<T> 的所有型別的集合, 使用 Concept 我們可以寫為

template <typename T>

concept unsigned_type = is_unsigned<T>::value;



template <unsigned_type T>

void func2(T a) {}

func2 的樣板引數有制約之後, 於是 func2 只能接受無號數型別. 對於 func2, 還有另外兩種形式的寫法 :

template <typename T> requires unsigned_type<T>

void func2(T a) {}

這是寫在了樣板參數之後, 還可以寫在函式參數列表之後 :

template <typename T>

void func2(T a) requires(unsigned_type<T>) {}

本質上這三種寫法是一樣的, 如果同時出現兩個或者以上, 編碼器會給出錯誤提示. 由於現在只有 GCC 實作了 Concept 部分, 因此此處我將展示 GCC 的錯誤提示

C++ 2a 特性導讀 – Concept-Jonny'Blog

我們可以看出, Concepts 邏輯上是一個布林常數表達式, 這個表達式的結果代表了一個型別是否符合 Concepts 的制約表達式的檢測結果

2. 制約表達式 (constraint-expression)

1. 結合制約表達式

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

template <typename T> requires(incrementable<T> and decrementable<T>)

void func(const T &) {}

2. 析取制約表達式

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

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

void func(const T &) {}

 

結合制約表達式和析取制約表達式都按照制約列表的順序從左到右進行計算, 對於結合制約表達式, 一旦某個不成立, 就會放棄後面的推斷; 對於析取制約表達式, 一旦某一個成立, 也會放棄後面的推斷

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 這種值, 因為 int * 並不是一個用戶自訂類別. 如果出現這種情況, 那麼程式就是病式的, 但是編碼器可以無需對其進行檢查. 由於這種屬於實作定義行為, 在 GCC 中, GCC 針對這種情況進行了檢查 :

template <typename T>

concept Foo = T::value or true;



template <Foo T>

void func(T) {}



int main(int argc, char *argv[]) {

    func((int *)0);        //Error : 'value' is not a member of 'int*'

}

制約表達式還可以針對摺疊表達式進行制約正規化, 但是具體是如何的, 我目前還沒有研究出來. 因為我所寫的所有關於 Concepts 和摺疊表達式的程式碼都沒有通過 GCC 編碼

 

除了上面所說的之外, 制約表達式還有一些額外值得注意的地方

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

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 函式的宣告, 但是宣告的時候調換了制約的順序, 這將會導致程式碼是病式的, 但是編碼器並不要求一定要針對此進行檢查, GCC 就沒有進行這樣的檢查

另外, 在制約表達式中, 隱含型別轉換是不生效的, 也就是任何可以通過隱含型別轉換而轉為 bool 型別的轉型都將被拒絕, 亦即制約表達式產生的是一個嚴格的 bool 型別右值 :

struct to_bool {

    constexpr operator bool() const noexcept {

        return true;

    }

};



template <typename T> requires(T {})

void func(T) {}



int main(int argc, char *argv[]) {

    func(to_bool {});

}

上述程式碼在 GCC 中會產生如下的編碼錯誤 :

C++ 2a 特性導讀 – Concept-Jonny'Blog

對於用戶自訂的 andor 運算子, 也就是多載的 and 或者 or 運算子, 在制約表達式的運算中是不生效的

3. requires 與 需求表達式 (requires-expression)

在制約表達式中, 我們已經展示了 requires 的其中一種用法, 這種用法是屬於 requires 陳述式, 和接下來要講的 requires 表達式 (我暫時稱之為需求表達式) 不相同, 它專門用於限制樣板引數的型別

我們有時候會需要標準程式庫中沒有實作的樣板, 例如 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 中還引入了需求表達式. 需求表達式的結果也是一個布林常數表達式, 它的語法是這樣的 :

template <template-parameter-list>

concept concept-name = requires[(requires-parameter-list)] {

    requires-sequence;

};

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

  • 簡約需求 (simple requirement)

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

template <typename T, typename U>

concept addable = requires(T t, U u) {

    t + u;

};



template <typename T, typename U> requires(addable<T, U>)

void func(T t, U u) {

    t + u;

}

Concept addable 要求型別 T 可以和型別 U 相加

template <typename T, typename U>

concept swappable = requires(T &&t, U &&u) {

    swap(t, u);

};



template <typename T, typename U> requires(swappable<T, U>)

void func(T &&t, U &&u) {

    swap(t, u);

}

Concept swappable 要求型別 T 可以和型別 U 進行交換, 也就是有對應的 swap 函式可以被呼叫

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

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

template <typename T> requires(++(T {}))

void func(T);

上述 Concept 要求 T 型別可以預設初始化並且支援自增加, 而這個制約就被直接放在了函式樣板的樣板參數列表之後

  • 型別需求 (type requirement)

型別要求用於檢查某個型別名稱是否合法, 但是型別必須被 typename 修飾, 用於告知編碼器這是一個型別而不是其它

template <typename T>

concept nested_type_check = requires {

    typename T::type;

};

Concept nested_type_check 用於檢查 T 類別內是否具有一個別名為 type 的型別

template <typename T>

concept template_specialization_check = requires {

    typename Foo<T>;

};

Concept template_specialization_check 用於檢查一個樣板特質化出來的是否為型別. 上述程式碼中, 若 Foo 是一個函式, 那麼會產生編碼錯誤, 因為 typename 要求 Foo 是一個型別

template <typename T>

using reference = typename add_lvalue_reference<T>::type;



template <typename T>

concept template_specialization_check = requires {

    typename reference<T>;

};

上述程式碼是用於檢測一個樣板別名特質化出來的是否為一個型別

  • 複合需求 (compound requirement)

符合需求類似於函式的宣告, 表示了一個表達式的基本屬性, 包括是否會擲出例外情況, 能不能滿足制約, 複合需求的基本形式為

{expression} [noexcept] [-> returning-type]

其中, {expression} 中的表達式實際上就是一個簡單需求, 而 noexcept 表示表達式是否滿足不擲出例外情況, 它是可選的; returning-type 同樣也是可選的, 它表示表達式最終的回傳型別應該是什麼型別或者應該滿足什麼制約

template <typename T>

concept returning_type_check = requires(T &&a) {

    {++a} noexcept -> typename T::type;

};

Concept returning_type_check 用於檢查 T 型別是否具有自增加的運算, 除此之外, 還要滿足自增加的運算擲出例外情況, 回傳型別必須為 T 類別內部的 type 型別

template <typename T>

concept unsigned_type = is_unsigned_v<T>;

template <typename T>

concept returning_type_check = requires(T &&a) {

    {++a -> unsigned_type<T>;

};

上述程式碼除了檢查 T 型別是否有自增加的運算之外, 回傳型別要滿足 unsigned_type 這一個 Concept 的制約. 上述程式碼暫時無法在 GCC 中通過, 我還要查閱一些資料才能確定它是否被納入了 C++ 20 標準

  • 巢狀需求 (nested requirement)
template <typename T, typename U>

concept same_type = is_same_v<T, U> and 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];

};

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

4. Concept 與多載解析

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

我們曾經在《C++ 學習筆記》中分析過, 函式樣板的匹配的過程中, 總是和最配對的那一個函式匹配. 那麼不難想像, 當 Concept 和多載結合的時候, 被制約的函式樣板在匹配的過程中, 總是和滿足更多制約的那一個匹配 :

#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

}

5. 評論

樣板超編程一直都是 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());

}

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

main.cpp:8:5 : error : cannot call std::sort with the type forward_list<int>::iterator (from f.begin()) and forward_list<int>::iterator (from f.end())
algorithm:column_for_std_sort:row_for_std_sort : note : the arguments of std::sort need to be two random-access-iterators

然而, 邪惡的 C++ 是這麼提示你的 (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 之後, 錯誤提示就會非常清楚 :

main.cpp:8:5 : error : cannot call std::sort with the type forward_list<int>::iterator (from f.begin()) and forward_list<int>::iterator (from f.end())
algorithm:column_for_std_sort:row_for_std_sort : note : concept RandomAccessIterator<forward_list<int>::iterator> was not satisfied

直接通過 Concept 的名稱就可以看出我們之前的程式碼有什麼錯誤了

總之, 早就該有的 Concept "千呼萬喚始出來", 我們都應該去學習這個重要的特性

6. 參考資料

cppreference.com : Constraints and concepts (since C++20)