EMCPP条款13:优先选用const_iterator,而非iterator

YiQi 管理员

const_iterator 是 STL 中相当千指涉到 const 的指针的等价物。它们指涉到不可被修改的值。只要有可能就应该使用 const 的标准实践表明,任何时候只要你需要一个迭代器而其指涉到的内容没有修改必要,你就应该使用 const_iterator

比如,假设你想在 std::vector<int> 中搜索第一次出现的 1983 (这一年,”C++”取代了”带类的C”成为了该种程序设计语言的名字),然后在此位置插入值 1998(这一年,首个ISO C++ 标准被接受)。如果 vector 中并无 1983, 那么插入位置将是其末尾处。在 C++98 中使用 itera tor 来实现,很容易:

1
2
3
4
std::vector<int> values;

std::vector<int>::iterator it = std::find(values.begin(),values.end(), 1983);
values.insert(it, 1998);

但在这里使用 iterator 并非正确选择,因为代码中并无任何地方修改了 iterator 指涉到的内容。修订这段代码以使用 const_iterator 本应是举手之劳 , 可是在 C++98 中,要费很大工夫。下面是一种在概念上貌似站得住脚的途径,但其实并不正确:

1
2
3
4
5
6
7
8
9
10
typedef std::vector<int>::iterator IterT;
typedef std::vector<int>::const_iterator ConstIterT;
std::vector<int> values;

ConstIterT ci = std::find(
static_cast<ConstIterT>(values.begin()),
static_cast<ConstIterT>(values.end()), // cast
1983
);
values.insert(static_cast<IterT>(ci), 1998); // may not compile;

C++11 中,这些现象彻底改观了。获取和使用 const_iterator 都变得容易了。

1
2
3
std::vector<int> values; // as before
auto it = std::find(values.cbegin(),values.cend(), 1983);
values.insert(it, 1998);

只有一种情景下,C++11 对于 const_iterator 的支持显得不够充分,那就是你想撰写最通用化的库代码的时候。这些代码会考虑到,某些容器,或类似容器的数据结构会以非成员函数的方式提供 beginend(还有 cbegincendrbegin 等),而不是以成员由数方式。所以,最通用化的代码会使用非成员函数,而不会假定其成员函数版本的存在性。

举例来说,刚才我们写的这段代码可以写成下面 findAndInsert 模板的通用形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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), // non-member cbegin
cend(container), // non-member cend
targetVal
);
container.insert(it, insertVal);
}

以上代码在 C++14 中可以完全正常运行,但可惜的是,在 C++11 中却不行。由于在标准化过程中的短视,C++11 仅添加了非成员函数版本的 beginend, 而没有添加 cbegincendrbeginrendcrbegincrend。C++14 纠正了这种短视。

要点速记

  • 优先选用 const_iterator, 而非 iterator
  • 在最通用的代码中,优先选用非成员函数版本的 beginendrbegin 等,而非其成员函数版本。
此页目录
EMCPP条款13:优先选用const_iterator,而非iterator