这是"Tag Dispatching"吗?

Is this "Tag Dispatching"?

本文关键字:Tag 这是 Dispatching      更新时间:2023-10-16

假设我有一些代码:

void barA() { }
void barB() { }
void fooA() {
  // Duplicate code...
  barA();
  // More duplicate code...
}
void fooB() {
  // Duplicate code...
  barB();
  // More duplicate code...
}
int main() {
  fooA();
  fooB();
}

我想删除fooAfooB之间的重复代码,我可以使用一些动态技术,如传入bool参数,传递函数指针或虚拟方法,但如果我想要一个编译时的技术,我可以这样做:

struct A { };
struct B { };
template<typename Tag> void bar();
template<> void bar<A>() { }
template<> void bar<B>() { }
template<typename Tag> void foo() {
  // Duplicate code
  bar<Tag>();
  // More duplicate code
}
int main() {
  foo<A>();
  foo<B>();
}

中,我引入了两个空的"标签"类来指示使用哪个bar,并基于标签类模板化foobar。这似乎奏效了。问题:

  1. 这个技术有名字吗?这是"标签调度"的一个例子吗?从我读到的关于标签调度的内容来看,它略有不同,涉及到带有标签参数的函数重载。一个可能来自trait类中的类型定义的标签。
  2. 是否有一种更典型的编译时技术来实现同样的事情?
编辑:

另一种可能性是使用bar的函数重载而不是模板特化,并将标记类作为参数传递:

struct A { };
struct B { };
void bar(A) { }
void bar(B) { }
template<typename Tag> void foo() {
  // Duplicate code
  bar(Tag());
  // More duplicate code
}
int main() {
  foo<A>();
  foo<B>();
}

这不是标签调度。正如你在你的问题中正确地说,如果你使用AB的一些编译时特性来区分两者,然后使用它来选择两种不同的重载。

标签调度的一个很好的例子是std::advance的典型实现方式。函数的签名是

template< class InputIt, class Distance >
void advance( InputIt& it, Distance n );
如果满足RandomAccessIterator的要求,

it可以在单次操作中增加n的位置。对于较小的迭代器,必须在循环中推进it。因此,实现可能会做类似于以下的事情:

namespace detail
{
  template<class InputIt, class Distance>
  void advance(InputIt& it, Distance n, std::random_access_iterator_tag) 
  {
    it += n;
  }
  template<class InputIt, class Distance>
  void advance(InputIt& it, Distance n, std::bidirectional_iterator_tag) 
  {
    if(n < 0) {
      while(n++) --it;
    } else {
      while(n--) ++it;
    }
  }
  template<class InputIt, class Distance>
  void advance(InputIt& it, Distance n, std::input_iterator_tag) 
  {
    assert(n >= 0);
    while(n--) ++it;
  }
}
template< class InputIt, class Distance >
void advance( InputIt& it, Distance n )
{
  detail::advance(it, n, 
                  typename std::iterator_traits<InputIt>::iterator_category());
}

我不知道你在做什么具体的名字。这只是一个如何遵循DRY原则的例子。

如果barAB的实例作为参数,那么我将以不同的方式实现它。我不想让bar成为一个函数模板,然后提供专门化,而是让重载解析为我完成这项工作。

void bar(A const&) { ... }
void bar(B const&) { ... }

但既然不是这样,提供显式专门化似乎是正确的方法。