std::forward_iterator_tag的作用是什么
What is the role of std::forward_iterator_tag?
在分析应用程序时,我偶然发现了gcc 4.7.1附带的标准库实现。它是include/g++-v4/bits/vector.tcc
:
template<typename _Tp, typename _Alloc>
template<typename _ForwardIterator>
void
vector<_Tp, _Alloc>::
_M_range_insert(iterator __position, _ForwardIterator __first,
_ForwardIterator __last, std::forward_iterator_tag)
{
…
}
我注意到函数签名的最后一个参数只是一个标记,我开始怀疑它为什么在这里。快速浏览此页面可以发现std::forward_iterator_tag
是一个空结构。它在这里的作用是什么?很明显,它对函数没有用处,而且可能会浪费寄存器或堆栈上的一些空间。那为什么呢?
简而言之:标签用于重载和优化
以一个简单的advance
为例,您可以设计:
template<class II, class D>
void advance(II& i, D n){
while( n-- ) ++i;
}
然而,它具有O(n)
复杂性,当您具有random_access_iterator
时,这是不可接受的。所以你可以这样改变你的设计:
template<class II, class D>
void advance_II(II& i, D n){
while( n-- ) ++i;
}
template<class RAI, class D>
void advance_RAI(RAI& i, D n){
i += n;
}
template<class II, class D>
void advance(II& i, D n){
if(is_random_access_iterator(i)) // not yet designed
advance_RAI(i, n);
else
advance_II(i, n);
}
然而,要使用的函数的版本是在运行时决定的,所以我们试图让编译器在编译时决定选择哪种方法。因此,我们给迭代器标签。有五个标签:
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirection_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirection_iterator_tag {};
现在你可以这样做了:
template<class II, class D>
void __advance(II& i, D n, input_iterator_tag){
while( n-- ) ++i;
}
template<class RAI, class D>
void __advance(RAI& i, D n, random_access_iterator_tag){
i += n;
}
template<class II, class D>
void advance(II& i, D n){
__advance(i, n, iterator_traits<II>::iterator_category());
}
区分不同的_M_range_insert
过载。
这个参考似乎好一点,并且有一个关于如何使用标记结构的示例。
冒引用链接的风险。。
空类将迭代器的类别标识为前向迭代器:
它被用作标记来标识迭代器的类型,以便函数(此处为_M_range_insert
)可以适当地操作。由于它是一个类型名称,因此可以用来触发不同的重载。
在我的impl中,我有
void _Insert(const_iterator _Where, _Iter _First, _Iter _Last,
_Int_iterator_tag)
void _Insert(const_iterator _Where, _Iter _First, _Iter _Last,
input_iterator_tag)
void _Insert(const_iterator _Where, _Iter _First, _Iter _Last,
forward_iterator_tag)
在其他过载中。
它是模板元编程机制的一部分,用于根据参数的特性选择适当的重载,因此,例如,如果您有随机访问迭代器,您可以利用这一点,在插入之前检查它们和保留之间的距离。另一方面,如果只有前向迭代器,则检查距离为O(n),因此不执行此操作,只执行后推操作,这可能导致多次重新定位,因此速度较慢。编译器也会优化掉这些空的结构,所以运行时不会受到惩罚。
有几个用于插入向量的重载,其中一些重载对于构造函数是重复的。如果向量元素是积分类型的,那么其中两个重载会发生冲突:
iterator insert( const_iterator pos, size_type count, const T& value );
template< class InputIt >
iterator insert( const_iterator pos, InputIt first, InputIt last );
如果您有一个vector<int>
并调用vec.insert(vec.bgein(), 5, 4)
,那么您的意思肯定是插入值4的5倍。但是重载解析将看到模板并调用该模板,从而推断InputIt
为int
。
为了解决这个问题和其他一些行为问题,标准库的实现者发明了一些特性和一堆标记类。这些特征是一个模板元函数,它将给出三个不同的标签,就像Karthik T在回答中所说的那样:
- 如果
InputIt
是积分类型,则为_Int_iterator_tag
forward_iterator_tag
,如果InputIt
是前向迭代器(包括随机访问迭代器)input_iterator_tag
如果InputIt
是非前向迭代器的迭代器类型
然后,您将有一堆_M_range_insert
的重载,将不同的标签类型作为附加参数,每个都做正确的事情,即
- Int标记的重载将重定向到
insert
(或其实现)的第一个非模板重载 - 前向迭代器的重载调用
reserve(std::distance(first,last))
,然后从迭代器范围复制元素 - 纯输入迭代器的重载只是复制元素,可能会导致多次重新分配。它不能调用
reserve
,因为输入迭代器只能求值一次(例如istream迭代器)
模板化的插入方法在概念上看起来如下:
template< class InputIt >
iterator insert( const_iterator pos, InputIt first, InputIt last )
{
return _M_range_insert(pos, first, last, InsertIteratorTraits<InputIt>::tag_type());
}
- "using namespace std;"在C++的作用是什么?
- 此代码中的" "是什么,它的基本作用是什么?
- 在模板参数中使用 {} 在 type_trait{} 中时,其作用是什么<T>?
- <<、>>算子在循环中的作用是什么
- 在c++中,减号和加号对字符串的作用是什么
- 窄集(_cast)的作用是什么
- 在这种情况下,#define 的作用是什么?
- 遍历模板参数包,没有争论的专业化的作用是什么?
- 在 c++ 中,没有 **std::fixed** 的 **std::setprecision()** 的作用是什么?
- 访问多个阵列时 CPU 缓存的作用是什么?
- 以下表达式的作用是什么:x = (a1,a2,..,n);
- 私人成员的作用是什么?
- 这段代码的作用是什么?
- *.exp文件的作用是什么
- ifstream::open()真正的作用是什么
- 如何在矩阵中使用指针.这行代码的作用是什么:int r=**(++b);
- 这个函数的作用是什么?与夹紧值有关
- 这个运算符()语法的作用是什么
- std::函数的模板参数中“const”修饰符和引用的作用是什么
- 字符串流在C++编程中的作用是什么