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

优先选择const_iterator,而非iterator

const_iterator在STL中相当于指向const的指针。它们指向不可被修改的值,只有由可能就应该使用const

任何时候只要你需要一个迭代器而其指向的内容没有修改必要,就应该使用const_iterator

在C++11中,获取和使用const_iterator变得很容易,容器的成员函数cbegin()cend()都返回const_iterator类型,甚至对于非const容器也是如此,并且STL成员函数若要取用指示位置的迭代器(例如插入或删除),它们也要求使用const_iterator类型

std::vector values;
...
auto it = std::find(values.cbegin(), values.cend(), 1983);
values.insert(it, 1998);

在撰写最通用化的库代码时,某些容器,或类似容器的数据结构会以非成员函数的方式提供beginend,而不是成员函数方式。

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

template
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仅添加了非成员版本的beginend,而没有添加cbegincendrbeginrendcrbegincrend

如果使用的是C++11,而且要撰写最通用化的代码,但使用的库中却没有提供成员函数版本的cbein系列缺失的模板。可以很容易地写出你自己的实现。

下面是非成员函数版本的cbegin的一个实现

template
auto cbegin(const C& container)->decltype(std::begin(container))
{
    return std::begin(container);
}

这个cbegin接受一个形参C,实参类型可以是任何表示类似容器的数据结构,并通过其引用到const类型的形参container来访问该实参。如果C对应一个传统容器类型(例如,std::vector),则container就是该容器类型的引用到const的版本(例如,const std::vector&)。调用非成员函数版本的begin函数并传入一个const容器会产生一个const_iterator,而模板返回的正是这个迭代器。这样实现的好处是它对于那些只提供了begin成员函数而未提供cbegin成员函数的容器也适用。

该模板在C是一个内建数组时也适用。在这种情况下,container成为了一个const数组的引用,C++11的非成员函数版本的begin为数组提供了一个特化版本,它返回一个指向数组首元素的指针。由于const数组的元素都是const,所以若给非成员函数版本的begin传入一个const数组,则其返回的指针是个指向const的指针。

要点速记

  • 优先选择const_iterator,而非iterator
  • 在最通用的代码中,优先选择非成员函数版本的beginendrbegin等,而非其成员函数版本

你可能感兴趣的:(effective,modern,C++——阅读笔记)