【发布时间】:2017-07-03 10:12:39
【问题描述】:
遵循向量上的“迭代时删除”模式之一,我不明白为什么这段代码有效,或者它是否使用了未定义的行为:
守则:
#include <vector>
#include <iostream>
int main(int argc, char* argv[], char* envz[])
{
std::vector<std::string> myVec;
myVec.push_back("1");
myVec.push_back("2");
myVec.push_back("3");
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
++i)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
--i;
continue;
}
std::cout << *i << std::endl;
}
return 0;
}
输出:
>g++ -g main.cpp
>./a.out
Erasing 1
2
3
问题:
考虑 for 循环的第一次迭代:
-
i是 myVec.begin(),它“指向”1。 - 我们进入条件块。
-
1被擦除,i被设置为被擦除元素的后一位,即2,现在 myVec.begin() 也指向它 - 我递减
i,所以现在它指向... myVec.begin() 之前的一个???
我很困惑为什么这似乎有效,正如输出所证明的那样,但是递减迭代器感觉有些可疑。如果条件为if ("2" == *i),则此代码很容易合理化,因为迭代器减量仍将其放置在向量中的有效条目处。 IE。如果我们有条件地删除2,i 将被设置为指向3,但随后手动递减并因此指向1,然后是for循环递增,将其设置为指向3再次。有条件地擦除最后一个元素同样容易理解。
我还尝试了什么:
这个观察让我假设在 vector::begin() 之前递减是幂等的,所以我尝试添加一个额外的递减,如下所示:
#include <vector>
#include <iostream>
int main(int argc, char* argv[], char* envz[])
{
std::vector<std::string> myVec;
myVec.push_back("1");
myVec.push_back("2");
myVec.push_back("3");
for (std::vector<std::string>::iterator i = myVec.begin();
i != myVec.end();
++i)
{
if ("1" == *i)
{
std::cout << "Erasing " << *i << std::endl;
i = myVec.erase(i);
--i;
--i; /*** I thought this would be idempotent ***/
continue;
}
std::cout << *i << std::endl;
}
return 0;
}
但这导致了段错误:
Erasing 1
Segmentation fault (core dumped)
有人可以解释为什么第一个代码块有效,特别是为什么擦除第一个元素后的单个减量有效吗?
【问题讨论】:
-
使用未定义行为的东西并不能保证它会崩溃 - 它有时可能会正常工作。
-
它并没有真正起作用,但未定义行为的一种可能影响是“似乎起作用”。如果
--i; ++i;导致i保持不变,这完全不足为奇。 -
为什么你会认为
--i是幂等的?!无论如何,如果您只是尝试使用支持调试迭代器的实现 like this 并看到它中止并显示有用的错误消息,您将节省大量时间。 -
@JonathanWakely 只是为了回答你的问题,我想知道
--i在指向vector::begin()时是否是幂等的,或者它是否指向某个神奇的“pre-begin()”持有点来自它可以合法地增加到begin()。但这既不存在也不存在:这段代码似乎依赖于未定义的行为。