TITLE: overloading complexity and return types [ This is part of a thread of discussion on overloading based upon the return type of the method. -adc ] (Newsgroups: comp.std.c++, 17 May 97) CLAMAGE: stephen.clamage@eng.sun.com > > The function overloading rules are amazingly complex and don't always > > produce the "expected" result even so. Programmers often have a > > difficult time determining which function will be called, and the > > problem is even worse for maintainers who didn't write the original > > code. NEWARD: Ted Neward > > I'm not sure that I agree that the function overloading rules are > amazingly complex. I *will* agree, however, that the "expected" result > often isn't. Still, anecdotal evidence leads me to believe that this is > an issue of good design and interface coding, and not the fault of the > language. CLAMAGE: The draft standard takes a full 25 pages (chapter 13) to explain the overloading rules. I call that amazingly complex. The complexity is due in part to the desire to do what most programmers would expect in most circumstances. The rules have been tweaked considerably over time, and still do not always give what seems like a reasonable result. Another large part of the complexity is due to interactions with other parts of the language. For example, when you write "x+y", the overloading rules must take into account the built-in versions of + with possible conversions from the types of x and y to built-in types, member functions (both static and non-static) of the class of x, stand-alone operator+ functions that are visible, plus functions defined in the same namespace as the types of x and y that might not otherwise be considered. You can mitigate the complexity in a program by writing only simple overloads, but that doesn't mean the language rules are not complex. Even so, just when you think you have a simple model, the rules can trip you up. Example: void g(int); void g(double); This works pretty well, until you find you have values of type "long", for which neither function is preferred. Add "void g(long);" and you still have ambiguity with unsigned values. CLAMAGE: > > Adding overloading on return type would make the rules unbearably > > complex and further reduce the likelihood of people writing and > > reading code correctly. NEWARD: > Again, I'm not sure I agree. Where the call would be ambiguous to the > reader, the compiler would also complain, and require either an explicit > cast or some other workaround. CLAMAGE: As in the above examples, often the programmer sees no ambiguity but the compiler does. The rules for multiple arguments are byzantine enough that it is hard for a programmer to decide whether there is a single best match, and if so, which one. This forum has seen heated discussions among experienced C++ programmers over such issues. Aguably, you have a bad design when this is the case. Exactly my point: the rules are complex. CLAMAGE: > > It would also introduce many more opportunities for ambiguous code. > > Trivial example: > > int f(); > > double f(); > > cout << f(); > > This example has to be ambiguous. > > NEWARD: > As somebody points out later in this thread, you change the cout call to > read: > > cout << (int)f(); // or cout << (double) f(); > // or int i = f(); cout << i; CLAMAGE: You can always use explicit casts to force a particular overloaded function to be called. But using casts is, or ought to be, a sign that something is wrong with your program. Further, casts do poorly as programs evolve. Continuing my function 'g' example: g((long)u); // u is type unsigned int, cast avoids ambiguity Now we add "void g(unsigned);" to the program. The above call still compiles without warnings and calls g(long) -- but without the cast would unambiguously call g(unsigned). Using casts is not a viable answer.