这是“标签分发”吗? [英] Is this "Tag Dispatching"?
问题描述
说我有一些代码:
void barA(){}
void barB(){}
void fooA(){
//复制代码...
barA();
//更多重复的代码...
}
void fooB(){
//复制代码...
barB();
//更多重复的代码...
}
int main(){
fooA();
fooB();
}
我要删除 fooA之间的重复代码
和 fooB
我可以使用一些动态技术,例如传递一个bool参数,传递一个函数指针或虚拟方法,编译时技术我可以这样做:
struct A {};
struct B {};
template< typename Tag> void bar();
模板<> void bar< A>(){}
template<> void bar< B>(){}
template< typename标签> void foo(){
//复制代码
bar< Tag>();
//更多重复的代码
}
int main(){
foo< A>();
foo< B>();
}
其中我已经引入了两个空的Tag类来表示 bar
以基于标记类使用和模板化 foo
和 bar
这似乎做的伎俩。问题:
- 这种技术有没有名字?这是标签调度的一个例子吗?根据我阅读的有关标记调度的内容,它略有不同,包括函数重载标签参数。
- 是否有更多的idomatic编译时技术实现同样的事情?
- Does this technique have a name? is this an example of "Tag dispatching"? From what I read about Tag dispatching it is slightly different and involves function overloading with a tag parameter. A tag that may have come from a typedef in a trait class.
- Is there a more idomatic compile-time technique of achieving the same thing?
ol>
编辑:
另一种可能是使用 bar
并传递标签类作为参数:
struct A {};
struct B {};
void bar(A){}
void bar(B){}
template< typename Tag> void foo(){
//复制代码
bar(Tag());
//更多重复的代码
}
int main(){
foo< A>();
foo< B>();
}
。正如你在你的问题中说的那样,如果你使用了 A
和 B
的编译时间trait区分两者,然后使用它们在两个不同的重载之间进行选择。
标签分发的一个很好的例子是 std :: advance
。函数的签名是
模板< class InputIt,class Distance>
void advance(InputIt& it,Distance n);
it
可以是高级 n
在单个操作中的位置,如果它满足RandomAccessIterator的要求。对于较小的迭代器,我们必须在循环中提前 it
。所以一个实现可能会做类似下面的事情:
命名空间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 ++)
} 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 原则的示例。
如果 bar
执行了 A
code> B 作为参数,那么我将实现不同。
$ b
void bar(A const&){...}
void bar(B const&){...}
但是事实并非如此,提供明确的专业化似乎是正确的方法。
Say I have some code:
void barA() { } void barB() { } void fooA() { // Duplicate code... barA(); // More duplicate code... } void fooB() { // Duplicate code... barB(); // More duplicate code... } int main() { fooA(); fooB(); }
And I want to remove the duplicate code between
fooA
andfooB
I could use a number of dynamic techniques such as passing in a bool parameter, passing a function pointer or virtual methods but if I wanted a compile time technique I could do something like this: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>(); }
where I have introduced two empty "Tag" classes to indicate which
bar
to use and templatedfoo
andbar
based on the tag class. This seems to do the trick. Questions:
Edit: Another possibility would be to use function overloading of
bar
instead of template specialization and pass the tag class as a parameter: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>(); }
解决方案This isn't tag dispatching. As you rightly said in your question, that'd be if you used some compile time trait of
A
andB
to distinguish between the two, and then use that to select between two different overloads.An good example of tag dispatch would be how
std::advance
is typically implemented. The function's signature istemplate< class InputIt, class Distance > void advance( InputIt& it, Distance n );
it
can be advancedn
positions in a single operation if it meets the requirements of RandomAccessIterator. For lesser iterators we must advanceit
in a loop. So an implementation would probably do something similar to the following: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()); }
I don't know of any specific name for what you're doing. It's just an example of how one would follow the DRY principle.
If
bar
took an instance ofA
andB
as an argument, then I'd implement this differently. Instead of makingbar
a function template, and then providing specializations, I'd let overload resolution do the job for me.void bar(A const&) { ... } void bar(B const&) { ... }
But since that's not the case, providing explicit specializations seems the right way to do this.
这篇关于这是“标签分发”吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!