標籤排程

在編譯時在函式之間進行選擇的一種簡單方法是將函式分派給一對過載的函式,這些函式將標記作為一個(通常是最後一個)引數。例如,要實現 std::advance(),我們可以在迭代器類別上排程:

namespace details {
    template <class RAIter, class Distance>
    void advance(RAIter& it, Distance n, std::random_access_iterator_tag) {
        it += n;
    }

    template <class BidirIter, class Distance>
    void advance(BidirIter& it, Distance n, std::bidirectional_iterator_tag) {
        if (n > 0) {
            while (n--) ++it;
        }
        else {
            while (n++) --it;
        }
    }

    template <class InputIter, class Distance>
    void advance(InputIter& it, Distance n, std::input_iterator_tag) {
        while (n--) {
            ++it;
        }
    }    
}

template <class Iter, class Distance>
void advance(Iter& it, Distance n) {
    details::advance(it, n, 
            typename std::iterator_traits<Iter>::iterator_category{} );
}

過載的 details::advance 函式的 std::XY_iterator_tag 引數是未使用的函式引數。實際的實現並不重要(實際上它是完全空的)。它們的唯一目的是允許編譯器根據呼叫哪個標記類 details::advance 來選擇過載。

在這個例子中,advance 使用 iterator_traits<T>::iterator_category 元函式,它返回 iterator_tag 類之一,具體取決於 Iter 的實際型別。然後,iterator_category<Iter>::type 的預設構造物件允許編譯器選擇 details::advance 的不同過載之一。 (這個函式引數可能會被完全優化掉,因為它是一個空的 struct 的預設構造物件,從未使用過。)

標籤排程可以為你提供比使用 SFINAE 和 enable_if 的等效程式碼更容易閱讀的程式碼。

注意:雖然 C++ 17 的 if constexpr 可能特別簡化了 advance 的實現,但它不適合與標籤排程不同的開放式實現。