parts/6.23.OperatorPrecedenceAndAssociativity-JCW.md

6.23 Operator Precedence and Associativity [JCW]

Applicability to language

The vulnerability as described in ISO/IEC TR 24772-1:2019 clause 6.23 is applicable to C++.

Operator precedence and associativity in C++ are determined by the C++ grammar. There are four operators that cannot be overloaded (user-defined):

Due to the large number of operators, one is recommended to consult an operator precedence table when needed, e.g., [https://en.cppreference.com/w/cpp/language/operator_precedence]. For example, in C++, the bitwise logical and shift operators are sometimes incorrectly treated as having the same precedence as arithmetic operations even though the bitwise operators have lower precedence. For example, the following (correct) expression subtracts one from x and then checks if the result is zero:

x - 1 == 0

which is equivalent to (x - 1) == 0,i.e., x - 1 is done first, then that result is compared to 0. Programmers mistakenly thinking the bitwise operations have the same precedence as arithmetic ones might write:

x & 1 == 0

intending to perform (x & 1) == 0, but precedence rules result in this evaluating x & (1 == 0) instead. This would have been easily fixed by using parenthesis to ensure the proper evaluation of an expression.

In addition to the aforementioned, C++ also permits operators to be overloaded when used with user-defined types. While it is not possible to change the precedence, associativity, and number of operands of overloaded operators [C++17, Clause 16.5 [over.oper], para. 6], overloaded operators can be executed differently than built-in operators. For example, overloaded operators lose any built-in operator short-circuiting properties and sequence order guarantees. Similarly overloaded operators and their arguments' evaluations behave as normal function calls, differing from built-in operator evaluation.

struct A {  };
bool operator&&(A const &, int);
int foo ();

void bar (A const & a)
{
  if (a     && foo());  // 'foo()' always evaluated
  if (false && foo());  // 'foo()' never evaluated
  if (a.operator&& (false,foo())); // 'foo()' always evaluated
}

Note that overloaded assignment falls into this category.

For issues related to the declaration of equality and relational operators see Clause 6.25 [KOA].

6.23.2 Avoidance mechanisms for language users

To avoid the vulnerability or mitigate its ill effects, C++ software developers can: