4

我无法为这个问题找到更好的标题,如果需要,请根据以下问题更新它。

考虑一个迭代器 - iter,指向 a 中的元素std::vector<int>。我*iter使用以下方法在当前迭代器位置插入值:

iter = target.insert(iter, *iter);

我知道这insert会将迭代器返回到新插入的元素。现在我把上面的语句改成:

iter = target.insert(iter, *iter++);

我知道这会表现得很尴尬,并导致Segmentation fault。我无法理解的是,如何评估上述作业?iter我的意思是,在该分配之后将指向哪个元素。据我了解,由于我使用了后增量运算符,它的行为方式应该与第一条语句类似,我从下面的示例分配中推断出:

i = i++;

由于上述赋值不会影响 的值i,因此在 的情况下也应如此*iter++

幕后的实际行为是什么?


这是真正的代码:

std::vector<int>::iterator begin = target.begin();

while (begin != target.end()) {
    if (*begin % 2 == 0) {
        begin = target.erase(begin);
    } else {
        begin = target.insert(begin, *begin); // I changed this line
        begin += 2;
    }
}

基本上上面的代码是从向量中删除偶数元素,并复制奇数元素。

编辑:

好的,如果我将第二行更改为++begin,除了之前的更改 - 它的工作方式与当前代码相同。所以,我else用这些行替换了块中的两行:

begin = target.insert(begin, *begin++);
++begin;

因此,似乎它分配的迭代器比前一种情况要快。

所以,这就是我从这种行为中理解的:

  • *begin++首先取消引用begin以获取当前位置的值。
  • begin是后递增的
  • 现在insert在后增量之后的迭代器之前插入取消引用的值。因此,它不是在原始 . 之前插入begin,而是在 . 之前插入++begin。然后返回值++begin,它插入的位置。

我是否正确解释了它?

4

2 回答 2

3

我不太明白您的问题,但要回应您列出的理解:您的理解在特定的 compiler/day/build 上可能是正确的。未指定是否插入 atbeginbegin + 1(这会导致返回的值insert是两个不同的可能值之一),因为编译器可以insert按照它喜欢的任何顺序评估参数。与其试图弄清楚会发生什么,不如将代码拆分成适当的部分,让您清楚地知道您要做什么,并让优化器为您处理事情。几乎可以肯定它核心转储的原因是因为当您增加 2 时,您会跳过end并且永远不会检测到您通过了容器的末尾。

我觉得我还应该指出,“幸运的是”在评估函数参数之后有一个序列点,或者begin = target.insert(begin, *begin++);是两次写入begin且没有中间序列点的未定义行为(例如i = i++是 UB)。

于 2013-07-10T16:06:11.360 回答
1

AFAIK 你构造 target.insert(iter, *iter++) 不是合法的 C++,因为它会导致“未定义的行为”。*iter++ 是合法的,并且从左到右进行评估,但未指定评估函数调用的参数的顺序。编写这样的表达式是一个编程错误。

准确地说,你的代码

target.insert(iter, *iter++)

给定别名

std::vector<int>::iterator cur = iter;
std::vector<int>::iterator next = iter + 1;

可以通过这两种方式进行评估

targets.insert(next, *cur);
++iter;

或者

targets.insert(cur, *cur);
++iter;

确切的版本取决于编译器、操作系统、其他代码等,通常没有指定。

于 2013-07-10T16:05:06.383 回答