11

我刚刚阅读了 C++ 标准,它std::for_each是一个非修改序列操作,以及findsearch等等。这是否意味着应用于每个元素的函数不应该修改它们?这是为什么?什么可能出错?

这是一个示例代码,其中修改了序列。你能看出它有什么问题吗?

void foo(int & i)
{
    i = 12;
}

int main()
{
    std::vector<int> v;
    v.push_back(0);

    std::for_each(v.begin(), v.end(), foo);
    // v now contains 12
}

我怀疑这只是一个解释问题,但我想听听你的看法。

PS:我知道我可以使用std::transform代替for_each,但这不是重点。

4

4 回答 4

21

很简单,您不能进行可能修改容器结构的更改。这是因为在一般情况下,修改容器会使正在使用的迭代器失效。

只要不改变容器的结构(例如容器中元素的顺序),就可以修改元素。

[添加]


for_each请注意,对于“非修改”算法似乎有些混淆。Stroustrup 在“The C++ Programming Language, 3rd Ed”第 4 次印刷的勘误表中总结了这种令人困惑的情况。(CPLfor_each )关于是否可以修改序列的元素有这个说法( http://www.research.att.com/~bs/3rd_printing5.html):

“该for_each()算法被归类为非修改算法,因为它没有显式修改序列。但是,如果应用于非常量序列for_each()可能会更改序列的元素。例如,请参阅negate()11.9 中的使用。” (最近的标准决议)。

CPL 最初表示for_each不允许传递给它的函数或函数对象修改传递给它的元素。但是,CPL 是在标准最终确定之前编写和最初发布的,显然在最终确定之前,这个限制就for_each()被删除了。

也可以看看:

于 2009-03-19T16:12:11.863 回答
16

看看他们说的这个缺陷报告

LWG 认为标准中没有任何内容禁止修改序列元素的函数对象。问题是 for_each 在名为“非变异算法”的部分中,并且标题可能令人困惑。非规范性说明应澄清这一点。

但也要注意这一点

他们似乎称其为“非修改”,因为 for_each 本身并没有显式修改序列的元素。

于 2009-03-19T16:24:38.433 回答
6

我认为“非修改序列操作”意味着此操作不修改序列。但是操作可以修改容器元素

容器元素和序列的价值 - 不同的东西。

于 2009-03-19T16:16:35.427 回答
2

如上所述,for_each 被归类为“非变异算法”

STL 的“变异”对应物是 std::transform。

由于您表示您知道您可以使用 std::transform,因此上述确实成为重点。它是与阅读您的代码的人的交流点。

如果我看到 std::for_each,很明显无论 foo 做什么,它都不会修改容器。

我遵循的指导方针可以说是:

“如果您希望使用容器的元素来执行某些不更改元素的任务,请使用 std::for_each。

如果您希望使用容器的元素以某种系统方式修改元素,或者在会以某种方式更改它们的任务中使用它们,请使用 std::transform。”

于 2009-03-19T21:49:50.727 回答