C++ 标签派遣

示例

在编译时在函数之间进行选择的一种简单方法是将一个函数分派给一对重载函数,这些重载函数将标记作为一个(通常是最后一个)参数。例如,要实现,我们可以分派迭代器类别: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{} );
}

所述std::XY_iterator_tag的重载参数details::advance函数是未使用的功能的参数。实际的实现并不重要(实际上它完全是空的)。它们的唯一目的是允许编译器根据details::advance调用哪个标记类来选择重载。

在此示例中,advance根据iterator_traits<T>::iterator_category返回iterator_tag的实际类型,使用元函数返回类之一Iter。然后使用默认构造的对象,iterator_category<Iter>::type编译器可以选择的不同重载之一details::advance。(此函数参数可能已被完全优化掉,因为它是一个默认构造的对象,为空struct,从未使用过。)

标记分派可以使您的代码比使用SFINAE和的等效代码更容易阅读enable_if。

注意:尽管C ++ 17if constexpr可能会advance特别简化实现,但与标记分派不同,它不适合开放实现。