const_iterator
是STL提供的一个等同于const
指针的东西,并且它所指向的值是不可变的。标准实践中,当你需要迭代器并且不需要更改迭代器指向的值的时候,你应该使用const_iterator
,无论是在C++98,还是C++11,这个标准实践都是正确的,可是在C++98中是并不完全支持const_iterator
。什么叫不完全支持呢?
例如,STL是有const_iterator
类型的迭代器,但是没有办法创建一个const_iterator
迭代器。容器类型只提供了begin(),end()
等返回iterator
的成员方法,std::find
返回的也是非常量版本的迭代器。或许有人认为可以通过static_cast
转化一下,所以有了下面这段代码:
typedef std::vector<int>::iterator IterT;
typedef std::vector<int>::const_iterator ConstIterT;
int main() {
std::vector<int> values;
ConstIterT ci = std::find(values.begin(),values.end(),198);
values.insert(static_cast<IterT>(ci), 198);
return 0;
}
上面的代码中std::find
返回的是非常量的迭代器,因为后续不会改动,遵从标准实践我通过static_cast
将其转化为常量迭代器,而当我在要往这个迭代器出插入一个元素的时候,我又不得不把它转换为非常量的迭代器,因为insert
的接受的就是非常量的迭代器,为此我又再次使用了神兵利器static_cast
,可是这次失败了,因为这是一个不可逆的转换,随后我尝试用reinterpret_cast,const_cast
都不行。
不过这一问题在C++11中得以解决,在C++11中可以很轻松的创建和使用常量迭代器,它给容器都提供了cbegin
和cend
等成员函数用于返回常量迭代器,修改容器中像insert
这种通过传递迭代器来标识位置的成员函数都修改成传递常量迭代器的版本。在C++11中上面的代码可以更改成下面的形式:
int main() {
std::vector<int> values;
auto it = std::find(values.cbegin(),values.cend(),198);
values.insert(it, 198);
return 0;
}
到此为止可以使用常量迭代器实现标准实践了。
虽然C++11提供了返回常量迭代器的成员函数cbegin
和cend
,但是这并不通用,因为除了标准容器外,其实还有一些类似于容器的数据结构,这些结构没有begin
,end
等这类方法,为此C++11中提供了非成员函数的版本,可以用于像数组这种类容器的数据结构。只有非成员函数版本的begin
,end
,对于写一些通用代码就更加的方便了。
template <typename C, typename V>
void findAndInsert(C& container,
const V& targetVal,
const V& insertVal)
{
using std::cbegin;
using std::end;
auto it = std::find(cbegin(container), cend(container), targetVal);
container.insert(it, insertVal);
}
上面的代码就比较通用了,对于一切类容器的数据结构都是适用的,而不是只能用于标准的STL
容器了。很可惜上面的代码需要C++14的支持,C++11只支持begin
和end
,而到C++14才开始支持cbegin
,cend
。如果你的编译器不支持C++14,那么你可以使用下面这段代码代替。
template <typename C>
auto cbegin(const C& container)->decltype(std::begin(container))
{
return std::begin(container);
}
上面这段代码的实现,巧妙了利用了模版类型推导的原则,具体可以参见Item1。