Undefined Behavior Revisited: Sequence Points and User-Defined Types
The Question:
In a previous installment on undefined behavior and sequence points, the expression i = i was deemed to invoke undefined behavior for built-in types. However, the question arises: what if the type of i is user-defined? Specifically, suppose we have a class Index with overloaded operators.
The Answer: Undefined Behavior vs. Well-Defined Behavior
Contrary to intuition, it turns out that the expression i = i does not invoke undefined behavior for user-defined types like Index. This is because the overloaded operators in Index are considered functions. According to the C ISO standard, function evaluation introduces a sequence point after the evaluation of the function's arguments, and another after copying the returned value.
Sequence Points and Overloaded Operators
In the case of i = i, the expression i is evaluated before it is passed as an argument to operator =. This means that there is a sequence point after the evaluation of i, ensuring that the object i is modified only once between consecutive sequence points.
Therefore, the expression i = i for user-defined types like Index is equivalent to writing i.operator =(i.operator ());, which is a well-defined expression with no undefined behavior. The same applies to the syntactically simpler expression i.add(i.inc());.
A Subtle Distinction:
It is important to note that i = i is not an expression in the sense of the C grammar. Instead, it is a "statement expression," which is a syntactic combination of an expression and a statement. Statement expressions are executed as normal statements, but their result can also be assigned to a variable.
Behavior of a[ i] = i
If a is an array of a built-in type, the expression a[ i] = i invokes undefined behavior because the lvalue a[ i] is evaluated twice: once in the expression i and again in the assignment i = i.
However, if a is a user-defined type that overloads the subscript operator, the expression may behave differently depending on the implementation of the subscript operator. For example, if the subscript operator is implemented using const Index& operator[](Index i), then the expression will be well-defined because the lvalue a[ i] is evaluated only once, before the subscript operator is called.
The Validity of i;
The expression i; is well-defined in C 03, as it is equivalent to ((i.operator ()).operator ()).operator (). This is because each operator introduces a sequence point, ensuring that the object i is modified only once between consecutive sequence points.
The above is the detailed content of Does `i = i` Invoke Undefined Behavior with User-Defined Types in C ?. For more information, please follow other related articles on the PHP Chinese website!