Why don't more mainstream statically typed languages support function/method overloading by return type? I can't think of any that do. It seems no less useful or reasonable than supporting overload by parameter type. How come it's so much less popular?
相关问题
- Dynamic Casts or Function Overloads?
- C++ operator lookup misunderstanding
- How to parse nested function calls using Pyparsing
- overloading a float to a numpy array
- Is divide by zero an error or an exception?
相关文章
- Generics and calling overloaded method from differ
- Overloading a super class's function
- Are there any reasons not to use “this” (“Self”, “
- Call/Return feature of classic C++(C with Classes)
- What are the major differences between C and C++ a
- Scala: arrays and type erasure
- Overload resolution with extern “C” linkage
- What set of functions is considered when resolving
If functions were overloaded by the return type and you had these two overloads
there is no way the compiler could figure out which of those two functions to call upon seeing a call like this
For this reason, language designers often disallow return-value overloading.
Some languages (such as MSIL), however, do allow overloading by return type. They too face the above difficulty of course, but they have workarounds, for which you'll have to consult their documentation.
This one is slightly different for C++; I don't know if it would be considered overloading by return type directly. It is more of a template specialization that acts in the manner of.
util.h
util.inl
util.cpp
This example is not exactly using function overload resolution by return type, however this c++ non object class is using template specialization to simulate function overload resolution by return type with a private static method.
Each of the
convertToType
functions are calling the function templatestringToValue()
and if you look at the implementation details or algorithm of this function template it is callinggetValue<T>( param, param )
and it is returning back a typeT
and storing it into aT*
that is passed into thestringToValue()
function template as one of its parameters.Other than something like this; C++ does not really have a mechanism to have function overloading resolution by return type. There may be other constructs or mechanisms that I'm not aware of that could simulate resolution by return type.
Contrary to what others are saying, overloading by return type is possible and is done by some modern languages. The usual objection is that in code like
you can't tell which
func()
is being called. This can be resolved in a few ways:int main() { (string)func(); }
.Two of the languages I regularly (ab)use overload by return type: Perl and Haskell. Let me describe what they do.
In Perl, there is a fundamental distinction between scalar and list context (and others, but we'll pretend there are two). Every built-in function in Perl can do different things depending on the context in which it is called. For example, the
join
operator forces list context (on the thing being joined) while thescalar
operator forces scalar context, so compare:Every operator in Perl does something in scalar context and something in list context, and they may be different, as illustrated. (This isn't just for random operators like
localtime
. If you use an array@a
in list context, it returns the array, while in scalar context, it returns the number of elements. So for exampleprint @a
prints out the elements, whileprint 0+@a
prints the size.) Furthermore, every operator can force a context, e.g. addition+
forces scalar context. Every entry inman perlfunc
documents this. For example, here is part of the entry forglob EXPR
:Now, what's the relation between list and scalar context? Well,
man perlfunc
saysso it's not a simple matter of having a single function, and then you do simple conversion at the end. In fact, I chose the
localtime
example for that reason.It's not just the built-ins that have this behavior. Any user can define such a function using
wantarray
, which allows you to distinguish between list, scalar, and void context. So, for example, you can decide to do nothing if you're being called in void context.Now, you may complain that this isn't true overloading by return value because you only have one function, which is told the context it's called in and then acts on that information. However, this is clearly equivalent (and analogous to how Perl doesn't allow usual overloading literally, but a function can just examine its arguments). Moreover, it nicely resolves the ambiguous situation mentioned at the beginning of this response. Perl doesn't complain that it doesn't know which method to call; it just calls it. All it has to do is figure out what context the function was called in, which is always possible:
(Note: I may sometimes say Perl operator when I mean function. This is not crucial to this discussion.)
Haskell takes the other approach, namely to not have side effects. It also has a strong type system, and so you can write code like the following:
This code reads a floating point number from standard input, and prints its square root. But what is surprising about this? Well, the type of
readLn
isreadLn :: Read a => IO a
. What this means is that for any type that can beRead
(formally, every type that is an instance of theRead
type class),readLn
can read it. How did Haskell know that I wanted to read a floating point number? Well, the type ofsqrt
issqrt :: Floating a => a -> a
, which essentially means thatsqrt
can only accept floating point numbers as inputs, and so Haskell inferred what I wanted.What happens when Haskell can't infer what I want? Well, there a few possibilities. If I don't use the return value at all, Haskell simply won't call the function in the first place. However, if I do use the return value, then Haskell will complain that it can't infer the type:
I can resolve the ambiguity by specifying the type I want:
Anyway, what this whole discussion means is that overloading by return value is possible and is done, which answers part of your question.
The other part of your question is why more languages don't do it. I'll let others answer that. However, a few comments: the principle reason is probably that the opportunity for confusion is truly greater here than in overloading by argument type. You can also look at rationales from individual languages:
Ada: "It might appear that the simplest overload resolution rule is to use everything - all information from as wide a context as possible - to resolve the overloaded reference. This rule may be simple, but it is not helpful. It requires the human reader to scan arbitrarily large pieces of text, and to make arbitrarily complex inferences (such as (g) above). We believe that a better rule is one that makes explicit the task a human reader or a compiler must perform, and that makes this task as natural for the human reader as possible."
C++ (subsection 7.4.1of Bjarne Stroustrup's "The C++ Programming Language"): "Return types are not considered in overload resolution. The reason is to keep resolution for an individual operator or function call context-independent. Consider:
If the return type were taken into account, it would no longer be possible to look at a call of
sqrt()
in isolation and determine which function was called." (Note, for comparison, that in Haskell there are no implicit conversions.)Java (Java Language Specification 9.4.1): "One of the inherited methods must must be return type substitutable for any other inherited method; otherwise, a compile-time error occurs." (Yes, I know this doesn't give a rationale. I'm sure the rationale is given by Gosling in "the Java Programming Language". Maybe someone has a copy? I bet it's the "principle of least surprise" in essence.) However, fun fact about Java: the JVM allows overloading by return value! This is used, for example, in Scala, and can be accessed directly through Java as well by playing around with internals.
PS. As a final note, it is actually possible to overload by return value in C++ with a trick. Witness:
In such a language, how would you resolve the following:
if
f
had overloadsvoid f(int)
andvoid f(string)
andg
had overloadsint g(int)
andstring g(int)
? You would need some kind of disambiguator.I think the situations where you might need this would be better served by choosing a new name for the function.
As already shown - ambiguous calls of a function that differs only by return type introduces ambiguity. Ambiguity induces defective code. Defective code must be avoided.
The complexity driven by the attempt to ambiguity shows that this is not a good hack. Apart from an intellectual exercise - why not use procedures with reference parameters.
this overloading feature is not hard to manage, if you look at it in a slightly different way. consider the following,
if a language did return overloading it would allow parameter overloading, but not duplications. this would solve the problem of:
because there is only one f(int choice) to choose from.