曾經在 《C++ 學習筆記》中, 我們簡單地講述了標準樣板程式庫中的疊代器. 疊代器分為五種, 這五種疊代器從上到下的功能越來越豐富. 隨著功能的增多, 我們希望根據疊代器種類的不同, 同一個函式做的事情也不同. 對於功能較多的疊代器, 某一些作業更加簡單一些; 對於功能比較少的疊代器, 可能要寫更多的程式碼

要解決上面這個需求非常簡單, C++ 中具有函式多載. 因此根據疊代器種類的不同, 讓函式的參數列表型別不同就可以了. 但是疊代器的種類是和型別相關的, 如若我們想要在編碼期解決這個問題, 於是就需要用到 Traits 技巧

對於 vector 的疊代器, 我們首先來看看其中一種實作的方法 :

template <typename T>

class vector_iterator {

public:

    using value_type = T;

    using pointer = T *;

    using const_pointer = const T *;

    using reference = T &;

    using const_reference = const T &;

    using rvalue_reference = T &&;

    using iterator_category = std::random_access_iterator_tag;

    //...

};

vector_iterator 這個類別中, 我們首先注意到 iterator_category 這個型別, 對於其它型別其實和 iterator_category 是差不多的. 標準樣板程式庫中, 有 5 個關於疊代器的標籤 : input_iterator_tag, output_iterator_tag, forward_iterator_tag, bidirectional_iterator_tagrandom_access_iterator_tag

vector_iterator 的疊代器相當於一個內建的指標, 內建的指標是一個隨機訪問疊代器, 因此 vector_iterator 這個類別的標籤也是隨機訪問疊代器

於是我們基本知道, 幾乎每一個疊代器的內部都會有一個 iterator_category 的標籤, 用來說明這個疊代器是什麼種類的疊代器. 我們就是根據這個標籤來對不同種類的疊代器進行區分, 然後在處理它們的時候使用不同的程式碼

非常自然地, 我們想到了我們需要一個介面, 這個介面的作用就是獲取這些型別, 獲取到之後, 根據型別的不同實作不一樣的程式碼. 因此, iterator_traits 這個類別就誕生了. 此處, 我將以一個簡單的 iterator_traits 類別作為示例 :

template <typename Iterator>

struct iterator_traits {

    using value_type = typename Iterator::value_type;

    using pointer = typename Iterator::pointer;

    using const_pointer = typename Iterator::const_pointer;

    using reference = typename Iterator::reference;

    using const_reference = typename Iterator::const_reference;

    using rvalue_reference = typename Iterator::rvalue_reference;

    using iterator_category = typename Iterator::iterator_category;

};

既然 iterator_traits 是一個介面, 因此當任意型別的疊代器放入 iterator_traits 的樣板參數的時候, 都應該保持有相同的結果. 我們不應該隨意在 iterator_traits 中增加其它型別, 比如 void_pointer 這樣的標識. 因為 void_pointer 這樣的標識可能只是某些疊代器所具備的, 而一般的疊代器只具備上述程式碼中的這些標識, 因此我們只能放入通用的標識, 否則就會產生編碼錯誤. 當然, 如果你只想要 iterator_traits 相容某一些疊代器, 而並不想適配別的疊代器, 那麼可以通過這個技巧, 加上你想放入的疊代器才具有的標識就可以了. 如果 iterator_traits 中加入了一些不應該加入的標識, 那麼就容易產生編碼錯誤

有了 iterator_traits 這個介面, 我們就可以獲取任意符合一定標準的疊代器 :

#include <iostream>



template <typename T>

class vector_iterator {

public:

    using value_type = T;

    using pointer = T *;

    using const_pointer = const T *;

    using reference = T &;

    using const_reference = const T &;

    using rvalue_reference = T &&;

    using iterator_category = std::random_access_iterator_tag;

    //...

};

template <typename Iterator>

struct iterator_traits {

    using value_type = typename Iterator::value_type;

    using pointer = typename Iterator::pointer;

    using const_pointer = typename Iterator::const_pointer;

    using reference = typename Iterator::reference;

    using const_reference = typename Iterator::const_reference;

    using rvalue_reference = typename Iterator::rvalue_reference;

    using iterator_category = typename Iterator::iterator_category;

};



template <typename Iter>

struct iterator_checking {

    void operator()() noexcept {

        std::cout << "other iterator or not iterator!" << std::endl;

    }

};

template <>

struct iterator_checking<std::random_access_iterator_tag> {

    void operator()() noexcept {

        std::cout << "random access iterator!" << std::endl;

    }

};



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

    iterator_checking<int> int_check;

    iterator_checking<typename iterator_traits<vector_iterator<int>>::iterator_category> vector_iterator_check;

    int_check();        //結果 : other iterator or not iterator!

    vector_iterator_check();        //結果 : random access iterator!

}

在上述程式碼中, 我們通過 iterator_checking 類別來檢查放入的型別是否為隨機訪問疊代器. iterator_checking 類別內部多載了函式呼叫運算子, 我們可以像呼叫函式一樣使用這種類別. 當我們放入 std::random_access_iterator_tag 這個型別的時候, 就會特質化到第二個類別, 第二個類別多載的函式呼叫運算子內部的輸出和未特質化的類別內部不同. 這就執行了型別是否為隨機訪問疊代器的檢查, 並且將最終的結果輸出

上面如果你明白了, 想像力豐富的你可能已經想到運用這一個特點和 C++ 的函式多載造飛機. 但實際上, 這還遠遠不夠, 因為大家可能會遇到函式樣板在匹配的時候被忽略等問題. 這還需要之後繼續學習, 才可以解決