优先选择const_iterator,而非iterator
const_iterator
在STL中相当于指向const
的指针。它们指向不可被修改的值,只有由可能就应该使用const
。
任何时候只要你需要一个迭代器而其指向的内容没有修改必要,就应该使用const_iterator
。
在C++11中,获取和使用const_iterator
变得很容易,容器的成员函数cbegin()
和cend()
都返回const_iterator
类型,甚至对于非const
容器也是如此,并且STL成员函数若要取用指示位置的迭代器(例如插入或删除),它们也要求使用const_iterator
类型。
std::vector<int> values;
...
auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998);
在撰写最通用化的库代码时,某些容器,或类似容器的数据结构会以非成员函数的方式提供begin
和end
,而不是成员函数方式。
举例来说,刚才写的代码可以写成下面findAndInsert模板的通用形式
template<typename C,typename V>
void findAndInsert(C& container,
const V& targetVal,
const V& insertVal)
{
using std::cbegin;
using std::cend;
auto it = std::find(cbegin(container),cend(container), targetVal);
container.insert(it, insertVal);
}
以上代码在C++14中可以完全正常运行,在C++11中却不行。C++11仅添加了非成员版本的begin
和end
,而没有添加cbegin
,cend
,rbegin
,rend
,crbegin
,crend
。
如果使用的是C++11,而且要撰写最通用化的代码,但使用的库中却没有提供成员函数版本的cbein
系列缺失的模板。可以很容易地写出你自己的实现。
下面是非成员函数版本的cbegin
的一个实现
template<class C>
auto cbegin(const C& container)->decltype(std::begin(container))
{
return std::begin(container);
}
这个cbegin
接受一个形参C,实参类型可以是任何表示类似容器的数据结构,并通过其引用到const
类型的形参container
来访问该实参。如果C对应一个传统容器类型(例如,std::vector<int>
),则container
就是该容器类型的引用到const
的版本(例如,const std::vector<int>&
)。调用非成员函数版本的begin
函数并传入一个const
容器会产生一个const_iterator
,而模板返回的正是这个迭代器。这样实现的好处是它对于那些只提供了begin
成员函数而未提供cbegin
成员函数的容器也适用。
该模板在C是一个内建数组时也适用。在这种情况下,container
成为了一个const
数组的引用,C++11的非成员函数版本的begin
为数组提供了一个特化版本,它返回一个指向数组首元素的指针。由于const
数组的元素都是const
,所以若给非成员函数版本的begin
传入一个const
数组,则其返回的指针是个指向const
的指针。
要点速记
- 优先选择
const_iterator
,而非iterator
- 在最通用的代码中,优先选择非成员函数版本的
begin
,end
和rbegin
等,而非其成员函数版本