[ Team LiB ] Previous Section Next Section

Gotcha #85: Precedence and Overloading

The precedence of an operator is part of the user's set of expectations about the operator's behavior, and if these expectations are not met, the operator will be misused. Consider a nonstandard complex number implementation:



class Complex { 


 public:


   Complex( double = 0, double = 0 );


   friend Complex operator +( const Complex &, const Complex & );


   friend Complex operator *( const Complex &, const Complex & );


   friend Complex operator ^( const Complex &, const Complex & );


   // . . .


};


We'd like to have an exponentiation operator for complex numbers, but C++ doesn't have an exponentiation operator. Since we can't introduce any new operators, we decide to press an existing operator into service that has no predefined meaning for complex numbers: exclusive-or.

In fact, we already have a problem, since an experienced C or C++ programmer will (properly) read an expression like a^b as a exclusive-ored with b rather than as a raised to the b power. However, there's also a more insidious problem:



a = -1 + e ^ (i*pi); 


In mathematical notation and in most programming languages that support exponentiation, the exponentiation operator has very high precedence. It's likely that the author of this code is expecting the expression to be parsed with the exponentiation binding more tightly than the addition:



a = -1 + (e ^ (i*pi)); 


In actuality, the compiler knows nothing about anyone's expectation of precedence of exponentiation. It sees an exclusive-or and parses the expression appropriately:



a = (-1 + e) ^ (i*pi); 


In this case, it's better to abandon operator overloading for the clearer use of a simple non-operator function:



a = -1 + pow( e, (i*pi) ); 


The precedence of an operator is part of its interface. Make sure an overloaded operator has a precedence that satisfies its users' expectations.

    [ Team LiB ] Previous Section Next Section