这段时间断断续续地重读C++ Primer,发现了一个比较有趣的点,309页的练习9.22这道题:
此题共有两个错误,一个是while陷入了死循环,因为insert()函数返回的是插入点之前的迭代器,因此while循环的条件永远为真,这是比较明显的;另一个错误是mid这个迭代器会失效,这是比较值得研究的点,对此github上Cpp-Primer项目的几个contributor也展开了讨论,现将其总结为本文。
如同书里315页的9.3.6所说
此处插入的点在vector的mid迭代器之前,那么必然会导致插入位置之后,也就是mid这个迭代器失效,怎么来判断迭代器失效了呢?看看下面的代码(运行环境:Clion):
void Func9_22()
{
vector<int> v1 = {0, 1, 2, 3, 4, 5};
auto m = [&] {return v1.begin() + v1.size() / 2};
vector<int>::iterator vb = v1.begin();
vector<int>::iterator vm = v1.begin() + v1.size() / 2;
v1.insert(vb, -1);
cout << "vb = " << *vb << endl;
cout << "vm = " << *vm << endl;
cout << "vb's position:" << vb - v1.begin() << endl;
cout << "vm's position:" << vm - v1.begin() << endl;
cout << "v1 = " << *v1.begin() << endl;
}
此时输出如下:
vb = 0
vm = 3
vb's position:-8
vm's position:-5
v1 = -1
显然,vector的begin()还是mid的位置都已经改变了(但是他们对应的值还是不变的)。
接下来再看看下面的代码:
#include <iostream>
#include <vector>
void double_and_insert(std::vector<int>& v, int some_val)
{
auto mid = [&]{ return v.begin() + v.size() / 2; };
for (auto curr = v.begin(); curr != mid(); ++curr)
if (*curr == some_val)
++(curr = v.insert(curr, 2 * some_val));
}
int main()
{
std::vector<int> v{ 1, 9, 1, 9, 9, 9, 1, 1 };
double_and_insert(v, 1);
for (auto i : v)
std::cout << i << std::endl;
}
这里最显著的差别就是mid的定义:
auto mid = [&]{ return v1.begin() + v1.size() / 2; };
这是C++的lambda表达式,相当于定义了一个mid的无参数函数,返回的是v1.begin() + v1.size() / 2,需要注意的是,这是一个动态的过程,即这个索引会在程序运行过程中动态改变,当插入了新的元素后,v1的size()发生了变化,此时mid也会随之改变。
一开始就定义mid这里,是一个静态的过程,即定义好mid后,就永久指向了定义时的索引,哪怕后面插入了新的元素,也不会发生改变。
vector<int>::iterator mid = v1.begin() + v1.size() / 2;