这是“Tag Dispatching”吗?

时间:2014-06-01 04:35:23

标签: c++ templates c++11 template-meta-programming

说我有一些代码:

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>();
}

我已经介绍了两个空的&#34; Tag&#34;用于根据标记类指示要使用bar以及模板化foobar的类。这似乎可以解决问题。问题:

  1. 这种技术有名字吗?这是&#34;标签调度的一个例子&#34;?从我读到的关于Tag dispatching的内容来看,它略有不同,并且涉及使用tag参数的函数重载。可能来自特征类中的typedef的标记。
  2. 是否有一种更为自觉的编译时技术来实现同样的目标?
  3. 修改 另一种可能性是使用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>();
    }
    

1 个答案:

答案 0 :(得分:6)

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

标记分发的一个很好的例子是std::advance通常是如何实现的。函数的签名是

template< class InputIt, class Distance >
void advance( InputIt& it, Distance n );
如果

it符合RandomAccessIterator的要求,则可以在单个操作中提前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&) { ... }

但由于情况并非如此,提供明确的专业化似乎是正确的方法。