Search code examples
c++c++11c++14language-lawyerundefined-behavior

Is there any difference with undefined behaviour between iterator and scalar object?


The topic about evaluation order says that following code leads to undefined behavior until C++17:

a[i] = i++;

This happens due to unspecified order while evaluating left and right parts of the assignment expression.

C++14 standard 1.9/15 says:

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined.

But what if we use std::vector and its iterator object instead of scalar object i?

std::vector<int> v = {1, 2};
auto it = v.begin();
*it = *it++;   // UB?

Is there undefined behaviour (until c++17) or not?


Solution

  • In situations when an iterator is a class, the behavior is well defined in all versions of the standard, assuming that it++ points to a valid location inside its container (which in your example it does).

    C++ translates *it++ to this sequence of two function calls:

    it.operator++(0).operator*();
    

    Function calls introduce sequencing, so all side effects of the actual ++ invoked inside operator++ on the primitive used as an iterator's implementation (probably, a raw pointer) must complete before the function exit.

    However, iterators are not required to be classes: they could be pointers, too:

    struct foo {
        typedef int* iterator;
        iterator begin() { return data; }
    private:
        int data[10];
    };
    

    The code looks the same, and it continues to compile, but now the behavior is undefined:

    foo f;
    auto it = f.begin();
    *it = *it++; // <<== This is UB
    

    You can guard against this by invoking ++ as a member function:

    std::vector<int> v = {1, 2};
    auto it = v.begin();
    *it = *it.operator++(0);
    

    When the iterator is actually a pointer, this code will fail to compile, rather than causing undefined behavior.