views:

588

answers:

4

I just read in the C++ standard that std::for_each is a non-modifying sequence operation, along with find, search and so on. Does that mean that the function applied to each element should not modify them? Why is that? What could possibly go wrong?

Here is a sample code, where the sequence is modified. Can you see anything wrong with it?

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
}

I suspect this to be just an interpretation issue, but I wanted to have your opinion about that.

PS: I know I could use std::transform instead of for_each, but that's not the point.

+14  A: 

Quite simply, you can't make a change that could modify the structure of the container. That's because in the general case, modifying a container can invalidate the iterators being used.

You can modify the element as long as it doesn't change the container's structure (such as the order of elements in the container).

[addition]


Note that there seems to be some confusion about for_each being a 'non-modifying' algorithm. This confusing situation is summed up here by Stroustrup in errata for the 4rd Printing of "The C++ Programming Language, 3rd Ed." (CPL) has this to say about whether for_each can modify the elements of a sequence (http://www.research.att.com/~bs/3rd_printing5.html):

"The for_each() algorithm is classified as nonmodifying because it doesn't explicitly modify a sequence. However, if applied to a non-const sequence for_each() may change the elements of the sequence. For an example, see the use of negate() in 11.9." (recent standards resolution).

The CPL originally indicated that the function or function object passed to for_each was not permitted to modify the element passed to it. However, the CPL was written and originally published before the standard was finalized, and apparently this restriction on for_each() was removed before it was finalized.

See also:

Michael Burr
+4  A: 

I think "Non-modifying sequence operations" mean that this operation not modify sequence. But operation could modify container elements.

Value of container elements and sequence - different things.

bb
+10  A: 

See this defect report they say

The LWG believes that nothing in the standard prohibits function objects that modify the sequence elements. The problem is that for_each is in a secion entitled "nonmutating algorithms", and the title may be confusing. A nonnormative note should clarify that.

But also note this one.

They seem to call it "non-modifying" because for_each itself does not exlicitly modify the elements of the sequence.

Johannes Schaub - litb
Yes, I just found the same issues list. As I supposed, it was just a matter of interpretation (but I really think they should add this clarifying note).
Luc Touraille
yeah i think too. it's not yet in n2800. let's see what the next draft contains. as the note has CD1 status, it's strange enough it's not in anymore :) i suspect they removed it because they want to work out a concept for it.
Johannes Schaub - litb
Nice find! In particular note this in the defect report: "The original STL document by Stepanov and Lee explicitly prohibited the function object from modifying its argument". I wasn't aware of that.
Michael Burr
+2  A: 

As litb indicated above, the for_each is classified as a 'nonmutating algorithm'

STL's 'mutating' counterpart to that is std::transform.

Since you indicated you know you can use std::transform, the above does indeed become the point. It serves as a point of communication to folks who read your code.

If I see std::for_each, it is clear that whatever foo does, it won't modify the container.

The guideline I follow can be stated:

"If you wish to use the elements of a container to do some task that does not change the elements, use std::for_each.

If you wish to use the elements of a container to modify the elements in some systemic manner or use them in a task that will change them in some manner, use std::transform."

AJ S.