TITLE: Operator overloading and argument matching PROBLEM: fouts@cello.hpl.hp.com (Marty Fouts), 27 Aug 93 The following (incorrect) program is the shortest example I've ever been able to generate which demonstrates the C++ feature which most "helps" me to add bugs to my programs. It took me two hours today to isolate a program's infinite loop to this example and then a half an hour of code reading with a buddy to figure out (ok, stumble over) the mistake I had made. I offer the program without comment for your amusement. There is a prize of one attabeing for the shortest correct explanation of what I did wrong and why the program ends up in an infinite loop. #include class aClass { public: char *message; aClass(const char *newmessage); ~aClass() { delete [] message; } }; aClass::aClass(const char *newmessage) { message = new char[strlen(newmessage)+1]; strcpy(message, newmessage); } const ostream& operator<<(const ostream& o, const aClass &a) { o << a.message; return o; } main() { aClass b("Hello"); cout << b; return 0; } [ This bug resulted in a rather lengthy discussion with different people posting quotations from the ARM to support thier points (whether or not this is a legal program). The problem here is that the operator << takes a CONST ostream, causing it to be the only match for the << in the implementation. Some compilers caught this as an error. Others created an executable with an infinite recursion. Here is the response from someone with a "good" compiler. -adc ] RESPONSE: ellis@parc.xerox.com (John Ellis) [ ... ] (*) The compile-time error occurs in the following function: const ostream& operator<< (const ostream& o, const aClass &a) { o << a.message; /* error */ return o; } Let's see why. In the scope of the erroneous line, there are two relevant declarations of overloaded operator<<: ostream& ostream::operator<< (const char*); /* from iostream.h */ const ostream& operator<< (const ostream& o, const aClass &a); ARM sections 13.1 and 13.2 describe the rules for selecting the correct overloaded function. First, member functions are treated as equivalent non-member functions taking "this" as a first argument: ostream& operator<< (ostream& this, const char*); const ostream& operator<< (const ostream& o, const aClass &a); Next, occurrences of "const" in formal-parameter declarations are effectively disregarded (they are used only as tie-breakers): ostream& operator<< (ostream& this, char*); const ostream& operator<< (ostream& o, aClass &a); Then the sequence of conversions needed to "match arguments" are considered. The first function requires no argument conversion, whereas the second function requires a user-defined conversion from "char*" to "aClass" via "aClass::aClass (char*)". Thus, the matching rules select the first function as the one to be called: ostream& operator<< (ostream& this, const char*); The first actual argument "o" of the function call "o << a.message" is declared "const", while the corresponding formal parameter is a non-const reference. This is the error -- a non-const reference cannot be initialized with a "const" (ARM 8.4.3).