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