Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't mySet.erase(it++) undefined behavior, or is it?

Accordint to this quite highly upvoted answer, the canonical way to iterate through a set erasing some elements is the following:

for (it = mySet.begin(); it != mySet.end(); ) {
    if (conditionToDelete(*it)) {
        mySet.erase(it++);
    }
    else {
        ++it;
    }
}

This, of course, is a result of C++03's set erase not returning an iterator. Otherwise one could write it = mySet.erase(it); It is also obvious that one can write

itToDelete = it++;
mySet.erase(itToDelete);

This question is not about how to delete elements while iterating. The question is why does the following line apparently not result in undefined behavior.

mySet.erase(it++);

At first I was sure this had to be UB, because I was thinking in the wrong way about postincrement. It is a common (but wrong) way to think of pre-increment as happening BEFORE rest of evaluation, and postincrement happening AFTER. Of course, this is wrong. Both postincrement and preincrement have the side effect of incrementing the variable. The difference is the value of those expressions.

That said, as far as I can remember, the C++ standard (at least the C++03 one) does not specify exactly when the side effect of postincrement will take place. So, unless we have a guarantee that if a function argument which is a postincrement expression will have its side effects in place before entering the function body, shouldn't this be UB? What exactly (standards-wise), if anything, prohibits the side-effect of it++ taking place after the iterator has been invalidated inside the function body?

Quotes from the standard would be very much welcome.

For an argument's sake let's also suppose that set's iterator is a built in type and this is actually operator ++, not the overloaded operator-function

like image 245
Armen Tsirunyan Avatar asked Oct 23 '13 15:10

Armen Tsirunyan


1 Answers

This is not undefined behavior in C++03 because there is a sequence point after all the function arguments are evaluated.

The draft standard that is closest to C++03 and that is publicly available is N1804, there is no public version of the draft standard from before that I can find but the Wikipedia article on sequence points used C++98 and c++03 as references and the phrases are consistent with the paragraphs below from N1804.

In section 1.9 Program execution paragraph 16 says (emphasis mine going forward):

When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. [...]

and later on in section 5.2.2 Function call paragraph 8 says:

The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.

like image 175
Shafik Yaghmour Avatar answered Oct 12 '22 20:10

Shafik Yaghmour