More Effective C++ | Item 21: Overload to avoid implicit type conversions Back to Item 20: Facilitate the return value optimization Continue to Item 22: Consider using op= instead of stand-alone op Item 21: Overload to avoid implicit type conversions. Here's some code that looks nothing if not eminently reasonable: class UPInt { // class for unlimited public: // precision integers UPInt(); UPInt(int value); ... }; // For an explanation of why the return value is const, // see Item E21 const UPInt operator+(const UPInt& lhs, const UPInt& rhs); UPInt upi1, upi2; ... UPInt upi3 = upi1 + upi2; There are no surprises here. upi1 and upi2 are both UPInt objects, so adding them together just calls operator+ for UPInts. Now consider these statements: upi3 = upi1 + 10; upi3 = 10 + upi2; These statements also succeed. They do so through the creation of temporary objects to convert the integer 10 into UPInts (see Item 19). It is convenient to have compilers perform these kinds of conversions, but the temporary objects created to make the conversions work are a cost we may not wish to bear. Just as most people want government benefits without having to pay for them, most C++ programmers want implicit type conversions without incurring any cost for temporaries. But without the computational equivalent of deficit spending, how can we do it? We can take a step back and recognize that our goal isn't really type conversion, it's being able to make calls to operator+ with a combination of UPInt and int arguments. Implicit type conversion happens to be a means to that end, but let us not confuse means and ends. There is another way to make mixed-type calls to operator+ succeed, and that's to eliminate the need for type conversions in the first place. If we want to be able to add UPInt and int objects, all we have to do is say so. We do it by declaring several functions, each with a different set of parameter types: const UPInt operator+(const UPInt& lhs, // add UPInt const UPInt& rhs); // and UPInt const UPInt operator+(const UPInt& lhs, // add UPInt int rhs); // and int const UPInt operator+(int lhs, // add int and const UPInt& rhs); // UPInt UPInt upi1, upi2; ... UPInt upi3 = upi1 + upi2; // fine, no temporary for // upi1 or upi2 upi3 = upi1 + 10; // fine, no temporary for // upi1 or 10 upi3 = 10 + upi2; // fine, no temporary for // 10 or upi2 Once you start overloading to eliminate type conversions, you run the risk of getting swept up in the passion of the moment and declaring functions like this: const UPInt operator+(int lhs, int rhs); // error! The thinking here is reasonable enough. For the types UPInt and int, we want to overload on all possible combinations for operator+. Given the three overloadings above, the only one missing is operator+ taking two int arguments, so we want to add it. Reasonable or not, there are rules to this C++ game, and one of them is that every overloaded operator must take at least one argument of a user-defined type. int isn't a user-defined type, so we can't overload an operator taking only arguments of that type. (If this rule didn't exist, programmers would be able to change the meaning of predefined operations, and that would surely lead to chaos. For example, the attempted overloading of operator+ above would change the meaning of addition on ints. Is that really something we want people to be able to do?) Overloading to avoid temporaries isn't limited to operator functions. For example, in most programs, you'll want to allow a string object everywhere a char* is acceptable, and vice versa. Similarly, if you're using a numerical class like complex (see Item 35), you'll want types like int and double to be valid anywhere a numerical object is. As a result, any function taking arguments of type string, char*, complex, etc., is a reasonable candidate for overloading to eliminate type conversions. Still, it's important to keep the 80-20 rule (see Item 16) in mind. There is no point in implementing a slew of overloaded functions unless you have good reason to believe that it will make a noticeable improvement in the overall efficiency of the programs that use them. Back to Item 20: Facilitate the return value optimization Continue to Item 22: Consider using op= instead of stand-alone op