我刚刚在C ++标准中读到std::for_each
是一个非修改序列操作,以及find
,search
等等。这是否意味着应用于每个元素的函数不应该修改它们?这是为什么?什么可能出错?
这是一个示例代码,其中修改了序列。你能看出它有什么问题吗?
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
,但这不是重点。
答案 0 :(得分:20)
很简单,您无法进行可以修改容器结构的更改。这是因为在一般情况下,修改容器会使正在使用的迭代器无效。
只要不改变容器的结构(例如容器中元素的顺序),就可以修改元素。
[加成]
请注意,似乎有一些关于for_each
是“非修改”算法的混淆。 Stroustrup在第四版“C ++编程语言,第3版”的勘误表中总结了这种令人困惑的情况。 ( CPL )可以说明for_each
是否可以修改序列的元素(http://www.research.att.com/~bs/3rd_printing5.html):
“
for_each()
算法被归类为非修改,因为它没有显式修改序列。但是,如果应用于非常量序列for_each()
,则可能会更改序列的元素。例如,请参阅11.9中negate()
的使用。“ (最近的标准解决方案)。
CPL最初表示传递给for_each
的函数或函数对象不允许修改传递给它的元素。但是,CPL是在标准最终确定之前编写并最初发布的,显然对for_each()
的限制在最终确定之前已被删除。
另见:
答案 1 :(得分:16)
请参阅this defect report他们说
LWG认为标准中没有任何内容禁止修改序列元素的函数对象。问题是for_each是一个名为“非突变算法”的部分,标题可能令人困惑。一份非规范性说明应澄清这一点。
但请注意this one。
他们似乎称之为“非修改”,因为for_each本身并不会明确地修改序列的元素。
答案 2 :(得分:6)
我认为“非修改序列操作”意味着此操作不会修改序列。但是操作可以修改容器元素。
容器元素和序列的值 - 不同的东西。
答案 3 :(得分:2)
如上所述,for_each被归类为“非突变算法”
STL的“变异”对应是std :: transform。
既然你表示你知道你可以使用std :: transform,那么上面确实成了重点。它可以作为阅读代码的人们的沟通点。
如果我看到std :: for_each,很明显无论foo做什么,它都不会修改容器。
我遵循的指南可以说明:
“如果您希望使用容器的元素来执行不会更改元素的任务,请使用std :: for_each。
如果您希望使用容器的元素以某种系统方式修改元素,或者在将以某种方式更改它们的任务中使用它们,请使用std :: transform。“