Function overloading by return type?

2018-12-31 06:40发布

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?

14条回答
伤终究还是伤i
2楼-- · 2018-12-31 07:10

In haskell it's possible even though it doesn't have function overloading. Haskell uses type classes. In a program you could see:

class Example a where
    example :: Integer -> a

instance Example Integer where  -- example is now implemented for Integer
    example :: Integer -> Integer
    example i = i * 10

Function overloading itself is not so popular. Mostly languages I've seen with it are C++, perhaps java and/or C#. In all dynamic languages it's a shorthand for:

define example:i
  ↑i type route:
    Integer = [↑i & 0xff]
    String = [↑i upper]


def example(i):
    if isinstance(i, int):
        return i & 0xff
    elif isinstance(i, str):
        return i.upper()

Therefore there's no much point in it. Most people aren't interested whether language can help you drop a single line per where ever you use it.

Pattern matching is somewhat similar to function overloading, and I guess sometimes work similarly. It's not common though because it is useful only for few programs and is tricky to implement on most of languages.

You see there's infinitely many other better easier-to-implement features to implement into the language, including:

  • Dynamic typing
  • Internal support for lists, dictionaries and unicode strings
  • Optimizations (JIT, type inferencing, compiling)
  • Integrated deployment tools
  • Library support
  • Community support and gathering places
  • Rich standard libraries
  • Good syntax
  • Read eval print loop
  • Support for reflective programming
查看更多
琉璃瓶的回忆
3楼-- · 2018-12-31 07:11

In .NET, sometimes we use one parameter to indicate the desired output from a generic result, and then made a conversion to get what we expect.

C#

public enum FooReturnType{
        IntType,
        StringType,
        WeaType
    }

    class Wea { 
        public override string ToString()
        {
            return "Wea class";
        }
    }

    public static object Foo(FooReturnType type){
        object result = null;
        if (type == FooReturnType.IntType) 
        {
            /*Int related actions*/
            result = 1;
        }
        else if (type == FooReturnType.StringType)
        {
            /*String related actions*/
            result = "Some important text";
        }
        else if (type == FooReturnType.WeaType)
        {
            /*Wea related actions*/
            result = new Wea();
        }
        return result;
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Expecting Int from Foo: " + Foo(FooReturnType.IntType));
        Console.WriteLine("Expecting String from Foo: " + Foo(FooReturnType.StringType));
        Console.WriteLine("Expecting Wea from Foo: " + Foo(FooReturnType.WeaType));
        Console.Read();
    }

Maybe this example could help too:

C++

    #include <iostream>

enum class FooReturnType{ //Only C++11
    IntType,
    StringType,
    WeaType
}_FooReturnType;

class Wea{
public:
    const char* ToString(){
        return "Wea class";
    }
};

void* Foo(FooReturnType type){
    void* result = 0;
    if (type == FooReturnType::IntType) //Only C++11
    {
        /*Int related actions*/
        result = (void*)1;
    }
    else if (type == FooReturnType::StringType) //Only C++11
    {
        /*String related actions*/
        result = (void*)"Some important text";
    }
    else if (type == FooReturnType::WeaType) //Only C++11
    {
        /*Wea related actions*/
        result = (void*)new Wea();
    }
    return result;
}

int main(int argc, char* argv[])
{
    int intReturn = (int)Foo(FooReturnType::IntType);
    const char* stringReturn = (const char*)Foo(FooReturnType::StringType);
    Wea *someWea = static_cast<Wea*>(Foo(FooReturnType::WeaType));
    std::cout << "Expecting Int from Foo: " << intReturn << std::endl;
    std::cout << "Expecting String from Foo: " << stringReturn << std::endl;
    std::cout << "Expecting Wea from Foo: " << someWea->ToString() << std::endl;
    delete someWea; // Don't leak oil!
    return 0;
}
查看更多
只若初见
4楼-- · 2018-12-31 07:13

To steal a C++ specific answer from another very similar question (dupe?):


Function return types don't come into play in overload resolution simply because Stroustrup (I assume with input from other C++ architects) wanted overload resolution to be 'context independent'. See 7.4.1 - "Overloading and Return Type" from the "C++ Programming Language, Third Edition".

The reason is to keep resolution for an individual operator or function call context-independent.

They wanted it to be based only on how the overload was called - not how the result was used (if it was used at all). Indeed, many functions are called without using the result or the result would be used as part of a larger expression. One factor that I'm sure came into play when they decided this was that if the return type was part of the resolution there would be many calls to overloaded functions that would need to be resolved with complex rules or would have to have the compiler throw an error that the call was ambiguous.

And, Lord knows, C++ overload resolution is complex enough as it stands...

查看更多
春风洒进眼中
5楼-- · 2018-12-31 07:16

if you want to overload methods with different return types, just add a dummy parameter with default value to allow the overload execution, but don't forget the parameter type should be different so the overload logic works next is an e.g on delphi:

type    
    myclass = class
    public
      function Funct1(dummy: string = EmptyStr): String; overload;
      function Funct1(dummy: Integer = -1): Integer; overload;
    end;

use it like this

procedure tester;
var yourobject : myclass;
  iValue: integer;
  sValue: string;
begin
  yourobject:= myclass.create;
  iValue:= yourobject.Funct1(); //this will call the func with integer result
  sValue:= yourobject.Funct1(); //this will call the func with string result
end;
查看更多
查无此人
6楼-- · 2018-12-31 07:17

Good answers! A.Rex's answer in particular is very detailed and instructive. As he points out, C++ does consider user-supplied type-conversion operators when compiling lhs = func(); (where func is really the name of a struct). My workaround is a bit different - not better, just different (although it's based on the same basic idea).

Whereas I had wanted to write...

template <typename T> inline T func() { abort(); return T(); }

template <> inline int func()
{ <<special code for int>> }

template <> inline double func()
{ <<special code for double>> }

.. etc, then ..

int x = func(); // ambiguous!
int x = func<int>(); // *also* ambiguous!?  you're just being difficult, g++!

I ended up with a solution that uses a parameterized struct (with T = the return type):

template <typename T>
struct func
{
    operator T()
    { abort(); return T(); } 
};

// explicit specializations for supported types
// (any code that includes this header can add more!)

template <> inline
func<int>::operator int()
{ <<special code for int>> }

template <> inline
func<double>::operator double()
{ <<special code for double>> }

.. etc, then ..

int x = func<int>(); // this is OK!
double d = func<double>(); // also OK :)

A benefit of this solution is that any code which includes these template definitions can add more specializations for more types. Also you can do partial specializations of the struct as needed. For example, if you wanted special handling for pointer types:

template <typename T>
struct func<T*>
{
    operator T*()
    { <<special handling for T*>> } 
};

As a negative, you can't write int x = func(); with my solution. You have to write int x = func<int>();. You have to explicitly say what the return type is, rather than asking the compiler to suss it out by looking at type conversion operators. I would say that "my" solution and A.Rex's both belong in a pareto-optimal front of ways to tackle this C++ dilemma :)

查看更多
深知你不懂我心
7楼-- · 2018-12-31 07:22

I think this is a GAP in modern C++ definition… why ?

int func();
double func();

// example 1. → defined
int i = func();

// example 2. → defined
double d = func();

// example 3. → NOT defined. error
void main() 
{
    func();
}

Why can a C++ compiler can not throw an error in example "3" and accept the code in example "1+2" ??

查看更多
登录 后发表回答