标签调度

在编译时在函数之间进行选择的一种简单方法是将函数分派给一对过载的函数,这些函数将标记作为一个(通常是最后一个)参数。例如,要实现 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 的实现,但它不适合与标签调度不同的开放式实现。