Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 499
αλεχολυτ Avatar asked Aug 19 '17 10:08

αλεχολυτ


People also ask

What causes undefined behavior C++?

In C/C++ bitwise shifting a value by a number of bits which is either a negative number or is greater than or equal to the total number of bits in this value results in undefined behavior.

Is unspecified behavior undefined behavior?

Unspecified behavior is different from undefined behavior. The latter is typically a result of an erroneous program construct or data, and no requirements are placed on the translation or execution of such constructs.


1 Answers

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.

like image 192
Sergey Kalinichenko Avatar answered Oct 28 '22 06:10

Sergey Kalinichenko