Item 13: 比从iterator优先使用const_iterator

本文翻译自modern effective
C++,由于水平有限,故不能确保翻译完全正确,欢迎建议错误。谢谢!

博客已经搬至这里啦

STL中的const_iterator等价于pointers-to-const(指向const值的指针)。它们对的价未克让修改。使用const的正统做法是,每当你无需改iterator指向的价的时刻,你还当运用const_iterator。

随即对C++98和C++11吧都是对的,但是当C++98中,const_iterator只好算是勉强支撑。大家鞭长莫及简单地创立它们,并且要你成立了一个const_iterator,你接纳的克就为限定了。举个例子,假而你想要找到std::vector中的第一独1983(用“C++”替代“C
with
Classes”来作为名字的那么同样年),并且于相当地方插入一个1998(这无异年,第一独IOS
C++标准于利用)。假诺vector中莫1983,插入的岗位应该是vector的末尾给。在C++98中,使用iterator来实现,这生简单:

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中却不略。那里发出一样栽方法,从概念上的话是牢靠的,可是其仍然免得法的:

typedef std::vector<int>::iterator IterT;           //typedef
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()),
              1983);

values.insert(static_cast<IterT>(ci), 1998);        //可能无法编译,详情看下面

typedef不是得的,可是她们为代码中的cast更易于写有。(如若您对自身胡用typedef代替Item
9中引进的别名注解(alias
declaration),这是以,那个例子显示的凡C++98的代码,而转变叫讲明(alias
declaration)是C++11负之初特点。)

当std::find调用中采取cast是坐values是一个non-const容器,然后以C++98中,这里没简单的方法于non-const容器中赢得一个const_iterator。cast不是得的,因为用此外点子来博const_iterator也是可能的(比如,你可拿values绑定到一个reference-to-const变量(就是const
T&类型的价值),然后在你的代码中用好值代替values就可以了),不过不管通过哪类艺术,通过一个non-const容器,获取其的const_iterator的长河都是至极曲折的。

假诺您得了const_iterator,事情变得越来越糟糕了,因为以C++98中,只有iterator才可以让插入(insertion)及删除(erasure)“定位”。const_iterator是休被接受的。这便是为啥,在点的代码中,我管const_iterator(我好不容器从std::find中拿到的)转换成为了iterator(传入一个const_iterator给insert将不可能编译)。

说实话,我受有之代码可能为无从编译,因为就选择static_cast(甚至是显然的凶手锏reinterpret_cast),使const_iterator转换成为iterator也是力不从心移植的。(这不是C++98的限量,在C++11受,也是这么的。无论其看起差不多如是不过移栽的,const_iterator都无法简单地变换到iterator。)这里发出局部然则移栽的方法来闹一个iterator(指向const_iterator指向的地方),不过他们还极度复杂,不通用,并且不值得以本书中研讨。除此之外,我望自己之看法可知分晓地于您传达:const_iterator在C++98中是只大累,它们不值得以。最终,开发人士都尽量不使const,只于必要之意况下利用其,而且每当C++98中,const_iterator太不实用了。

每当C++11受,一切都更换了。现在const_iterator已经换得易得同易于接纳了。容器(虽然是non-const容器)的分子函数cbegin和cend发生一个const_iterator,并且原本在STL中,只以iterator定位(比如,inset和erase)的分子函数现在也克使const_iterator来定位了。把先前时期使用iterator的C++98版本的代码修改成拔取const_iterator的C++11本子的代码真是太简单了:

std::vector<int> values;

...

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

values.insert(it, 1998);

前天,代码用上了实用的const_iterator。

在C++11中,对于const_iterator的支撑,唯一不足的状就是当你想写一个最老限度的通用库的时。比起让客户利用成员函数,那样的库代码需要考虑啊容器与“类容器”提供non-member版本的begin和end(加上cbegin,cend,rbegin等)。举个例子,为了built-in数组需要如此做,为了局部单单供接口(包含有函数)的老三着库也要如此做。由此最好老限度的通用库需要提供non-member版本的函数,而不是去要有“容器”都发出成员函数。

推个例证,大家会将咱谈谈的事物长到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),      //non-member版本的cbegin
                        cend(container),        //non-member版本的cend
                        targetVal);

    container.insert(it, insertVal);
}

即刻当C++14饱受劳作得死去活来好,可是,很不满,在C++11可无计可施充足好地劳作。由于制定规范时的忽视,C++11才补充加了non-member版本的begin和end函数,可是她们不曾长相应的cbegin,cend,rbegin,rend,crbegin,crend。C++14再次凑巧了这问题。

如果你下C++11,你还要想写来最为特别限度的通用代码,并且在你利用的库中,没有一个储藏室提供这个为遗漏的cbegin(non-member版本的)。那么朋友,你可以轻松地形容起你协调的实现,举个例证,这里出一个non-member版本的cbegin的贯彻:

template<class C>
auto cbegin(const C& container)->decltype(std::begin(container))
{
    return std::begin(container);               //看下面的解释
}

盼non-member版本的cbegin没有调用member版本的cbegin,你认为非常奇怪是吧?我为觉得意外,但是随着代码看下。cbegin模板接受其他项目标参数来表示一个“类容器”(C),并且它经过她的reference-to-const形参(container)来利用实参。假若C是一个通常的器皿类(比如,一个std::vector),container将改为一个针对const容器的援(也虽然是,const
std::vector<int>&)。用const容器调用non-member版本的begin函数(由C++11供)就会生出一个const_iterator,并且这iterator就是那模板的回到值。用如此的点子来落实的优点是,对于那多少个提供了begin成员函数,不过没提供cbegin成员函数的器皿,能重复好地工作(在C++11的non-member版本的begin中,会调用这几个容器的begin成员函数)。因而,你可知对只提供begin成员函数的器皿,使用这non-member版本的cbegin。

设若C是一个built-in数组类型,那些模板也可以工作。在这种场地下,container成为一个对准const数组的援。C++11每当non-member版本的begin中,为数组提供了一个特殊之版,那多少个版本的begin重返一个针对数组中率先只要素的指针。一个const数组的元素是const的,所以non-member版本的begin为const数组再次来到一个point-to-const的指针,并且实际,一个point-to-const的指针对于数组来说就是一个const_iterator。(为了深刻摸底一个模板怎么呢built-in数组特殊化,请圈Item
1中,以靠为数组的援为参数的template类型推导的议论。)

唯独话说回来,那些Item的第一是,鼓励你,每当你可以使用const_iterator时,就失行使她。最初的心劲是,只要来必要,就要使const,但是以C++11事先的C++98中,配合iterator来使用const很不实用。而于C++11遭受,它怪实用,并且C++14填写了少量C++11遗留下来的坑(一小片段不兑现的物)。

            你一旦牢记的事
  • 于打iterator优先使用const_iterator
  • 于无限特别限度的通用代码中,比从成员函数,优先利用non-member版本的begin,end,rbegin等等。