This rule is part of MISRA C++:2023.
Usage of this content is governed by Sonar’s terms and conditions. Redistribution is
prohibited.
Rule 16.6.1 - Symmetrical operators should only be implemented as non-member functions
Category: Advisory
Analysis: Decidable,Single Translation Unit
Amplification
The following member binary operators are symmetrical operators, even when their parameters have different types:
operator+ operator- operator* operator/ operator%
operator== operator!= operator< operator<= operator>= operator>
operator^ operator& operator|
operator&& operator||
Rationale
This rule helps to ensure that both operands of a op b are treated identically in terms of conversions.
If operator+ for class C is implemented as a member (e.g. C operator+( C rhs ) const;), then the left-hand
value can only be of type C, whilst the compiler may implicitly convert right-hand operands of other types to C.
For example, if C has a constructor that takes an int value, then if c is a value of type C,
c + 1 creates a temporary object from C( 1 ) and adds it to c. Depending upon what other, if any, implicit
conversions are available, 1 + c either results in a compilation error or it may call a different function and give an entirely different
result to c + 1. This inconsistent behaviour is undesirable.
Note: this rule permits a non-member operator to be declared as a hidden friend (i.e. a friend function defined in the class).
Hidden friend operators are only considered for overload resolution by argument-dependent lookup when the compiler has a class object as one of the
operands, making it less likely that the wrong overload is selected due to the implicit conversion of both operands. The use of hidden friends for
such operators is generally considered to be good practice.
Example
The constructor in the following example violates M23_173: MISRA C++ 2023 Rule 15.1.3.
class C
{
int32_t i;
public:
C( int32_t x = 0 ): i( x ) {}
C operator+( C rhs ) const; // Non-compliant
C operator+( int32_t rhs ) const; // Non-compliant
C operator and( C rhs ) const; // Non-compliant
friend C operator*( C lhs, C rhs ); // Compliant - non-member friend
friend C operator-( C lhs, C rhs ) // Compliant - hidden friend
{
return C( lhs.i - rhs.i );
}
friend std::ostream &
operator<<( std::ostream & os,
C const & c ); // Rule does not apply - not symmetrical
C & operator/=( C const & rhs ); // Rule does not apply - not symmetrical
};
C operator/( C lhs, C rhs ); // Compliant - non-member
C operator*( C lhs, C rhs ); // Compliant - non-member friend
int main()
{
C c( 21 );
std::cout << ( c + 1 ) << '\n';
//std::cout << ( 1 + c ) << '\n'; // Would fail to compile
std::cout << ( c * 4 ) << '\n';
std::cout << ( 4 * c ) << '\n';
std::cout << ( 84 / c ) << '\n';
}
Copyright The MISRA Consortium Limited © 2023