修改 BOOST_FOREACH 中向量的内容

Posted

技术标签:

【中文标题】修改 BOOST_FOREACH 中向量的内容【英文标题】:Modifying contents of vector in BOOST_FOREACH 【发布时间】:2009-11-26 02:30:49 【问题描述】:

这是一个关于 BOOST_FOREACH 如何检查其循环终止的问题

cout << "Testing BOOST_FOREACH" << endl;
vector<int> numbers; numbers.reserve(8);
numbers.push_back(1); numbers.push_back(2); numbers.push_back(3);
cout << "capacity = " << numbers.capacity() << endl;
BOOST_FOREACH(int elem, numbers)

    cout << elem << endl;
    if (elem == 2) numbers.push_back(4); 

cout << "capacity = " << numbers.capacity() << endl;

给出输出

Testing BOOST_FOREACH
capacity = 8
1
2
3
capacity = 8

但是在循环中途插入的数字 4 呢?如果我将类型更改为列表,则新插入的数字将被迭代。如果需要重新分配,向量 push_back 操作将使任何指针无效,但是在此示例中不会发生这种情况。所以我猜的问题是,为什么 end() 迭代器在使用向量时似乎只被评估一次(在循环之前),但在使用列表时具有更动态的评估?

【问题讨论】:

我得到一个运行你的代码的断言,Visual Studio 2008。 @Gman:要让它在 Visual Studio 2008 下工作,您必须禁用迭代器调试 (/D_HAS_ITERATOR_DEBUGGING=0)。 这不是有点违背目的吗?您正在未定义的行为领域做某事。 修复警告,不要让它们静音。这就像有一个编译器会在您超出数组边界时向您发出警告,但您将其禁用以便您无论如何都可以这样做。 我并没有说这在实践中是一个好主意(这不是,我同意;这是一个糟糕的主意);我刚刚说过,为了让他的示例正常工作,需要禁用迭代器调试。 糟糕,我读了“Ja-”并假设你是 OP。愚蠢的我仍然不习惯寻找突出显示的提问者姓名。好的,我假设你知道警告。 :P 【参考方案1】:

在幕后,BOOST_FOREACH 使用 遍历元素的迭代器 顺序。在循环执行之前, 结束迭代器缓存在本地 多变的。这称为提升,并且 这是一个重要的优化。它 然而,假设结束 序列的迭代器是稳定的。它 通常是,但如果我们修改 通过添加或删除序列 迭代时的元素 它,我们最终可能会提升自己 在我们自己的宠物上。

http://www.boost.org/doc/libs/1_40_0/doc/html/foreach/pitfalls.html

如果您不希望 end() 迭代器更改,请在向量上使用调整大小而不是保留。

http://www.cplusplus.com/reference/stl/vector/resize/

请注意,您不想 push_back 而是使用 operator[] 代替。但要小心越界。

【讨论】:

对,对不起,以为他在问为什么尺寸没有改变 =) 改变我的答案 =) 来自同一个链接:故事的寓意是在从您正在迭代的序列中添加和删除元素之前要三思而后行。如果这样做可能导致迭代器无效,请不要这样做。请改用常规的 for 循环。 啊,这解释了很多,除了为什么它适用于列表?我的猜测是列表的 .end() 实际上返回 NULL (或类似的常量),因为列表是作为双向链表实现的......所以列表的最后一个元素被定义为 .next() 返回 null 的元素添加/删除元素时不会改变。我在正确的轨道上吗? 不确定 std::list 将什么用作 end(),但这没关系。但是 sgi 网站对列表这样说:sgi.com/tech/stl/List.html“列表具有重要的属性,即插入和拼接不会使列表元素的迭代器失效,即使删除也会使指向被删除元素的迭代器失效。”并且 end() 是列表的迭代器,因此它不应因插入操作而失效。【参考方案2】:

在 cmets 中提出了一个问题,即为什么 Microsoft 调试运行时在迭代向量而不是列表时会引发断言。原因是insertlistvector 的定义不同(注意push_back 只是序列末尾的insert)。

根据 C++ 标准(ISO/IEC 14882:2003 23.2.4.3,向量修饰符):

[插入时],如果没有发生重新分配,插入点之前的所有迭代器和引用都保持有效。

(23.2.2.3, 列表修饰符):

[insert] 不影响迭代器和引用的有效性。

因此,如果您使用 push_back(并确保它不会导致重新分配),则任何一个容器都可以继续使用您的迭代器来迭代序列的其余部分。

然而,在向量的情况下,使用在push_back 之前获得的end 迭代器是未定义的行为

这是对问题的迂回回答;这是对问题 cmets 中讨论的直接回答。

【讨论】:

【参考方案3】:

boost 的 foreach 将在 iterator == numbers.end() 时终止

但请注意,调用 push_back 可以/将使您拥有的任何当前迭代器无效。

【讨论】:

不是任何,只有在插入之后的那些,除非向量超出其capacity()(并注意他如何使用reserve())。

以上是关于修改 BOOST_FOREACH 中向量的内容的主要内容,如果未能解决你的问题,请参考以下文章

BOOST_FOREACH

shared_ptr 未能推送到向量

用BOOST_FOREACH简化遍历操作

BOOST_FOREACH宏强制方法退出Visual Studio命名空间

如何输出特征向量机器学习分类算法

向量内的不可修改值?