函数重载按返回类型?

时间:2009-01-14 05:38:05

标签: programming-languages language-design overloading function-calls

为什么更多主流的静态类型语言不支持返回类型的函数/方法重载?我想不出那样做。通过参数类型支持过载似乎没有那么有用或合理。怎么这么不受欢迎呢?

14 个答案:

答案 0 :(得分:514)

答案 1 :(得分:35)

如果函数被返回类型重载并且你有这两个重载

int func();
string func();

编译器无法确定在看到像这样的调用时调用这两个函数中的哪一个

void main() 
{
    func();
}

出于这个原因,语言设计者经常不允许返回值重载。

但是,某些语言(例如MSIL) do 允许按返回类型重载。他们当然也面临上述困难,但他们有解决方法,你必须查阅他们的文档。

答案 2 :(得分:27)

在这种语言中,您将如何解决以下问题:

f(g(x))

如果f有超载void f(int)void f(string)g有超载int g(int)string g(int)?你需要某种消除歧义。

我认为通过为函数选择新名称可以更好地满足您可能需要的情况。

答案 3 :(得分:18)

窃取特定于C ++的answer from another very similar question(欺骗?):


函数返回类型不会在重载决策中发挥作用,因为Stroustrup(我假设其他C ++架构师的输入)希望重载解析为'上下文无关'。请参阅“C ++编程语言,第三版”中的7.4.1 - “重载和返回类型”。

  

原因是要保持单个运算符或函数调用的分辨率与上下文无关。

他们希望它仅基于如何调用重载 - 而不是如何使用结果(如果它被使用的话)。实际上,在不使用结果的情况下调用许多函数,或者结果将被用作更大表达式的一部分。当我们确定这一点时,我肯定会发挥作用的一个因素是,如果返回类型是解决方案的一部分,那么将会有许多调用重载函数需要使用复杂规则解决或者必须让编译器抛出这个电话不明确的错误。

而且,主知道,C ++重载分辨率足够复杂,因为它......

答案 4 :(得分:5)

在haskell中,即使它没有函数重载也是可能的。 Haskell使用类型类。在程序中你可以看到:

class Example a where
    example :: Integer -> a

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

功能重载本身并不那么受欢迎。我见过的大部分语言都是C ++,也许是java和/或C#。在所有动态语言中,它都是:

的简写
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()

因此,没有太多意义。大多数人都不感兴趣,语言是否可以帮助您在任何使用它的地方放弃一行。

模式匹配有点类似于函数重载,我猜有时候工作方式类似。这并不常见,因为它只对少数几个程序有用,而且在大多数语言上都很难实现。

你会发现有很多其他更容易实现的功能可以在语言中实现,包括:

  • 动态类型
  • 对列表,词典和unicode字符串的内部支持
  • 优化(JIT,类型推理,编译)
  • 集成部署工具
  • 图书馆支持
  • 社区支持和聚会场所
  • 丰富的标准库
  • 语法良好
  • 阅读eval print loop
  • 支持反思式编程

答案 5 :(得分:3)

好的答案! A.Rex的回答尤其非常详细和有启发性。正如他所指出的,在编译lhs = func(); (其中func实际上是结构的名称)时,C ++ 会考虑用户提供的类型转换运算符。我的解决方法有点不同 - 不是更好,只是不同(虽然它基于相同的基本想法)。

而我想要写...

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++!

我最终得到了一个使用参数化结构的解决方案(T =返回类型):

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 :)

此解决方案的一个好处是,包含这些模板定义的任何代码都可以为更多类型添加更多特化。您还可以根据需要对结构进行部分特化。例如,如果您想要对指针类型进行特殊处理:

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

作为否定,你不能用我的解决方案写int x = func();。你必须写int x = func<int>();。您必须明确说出返回类型是什么,而不是要求编译器通过查看类型转换运算符来解决它。我会说“我的”解决方案和A.Rex都属于pareto-optimal front方法来解决这个C ++难题:)

答案 6 :(得分:1)

如果要重载具有不同返回类型的方法,只需添加一个带有默认值的虚拟参数以允许重载执行,但不要忘记参数类型应该是不同的,所以过载逻辑接下来的工作是例如delphi:

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

像这样使用

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;

答案 7 :(得分:0)

如前所示 - 仅仅通过返回类型而不同的函数的模糊调用引入了歧义。 模糊性导致有缺陷的代码。 必须避免使用有缺陷的代码。

由模糊性尝试驱动的复杂性表明这不是一个好的黑客。 除了智力练习 - 为什么不使用参考参数的程序。

procedure(reference string){};
procedure(reference int){};
string blah;
procedure(blah)

答案 8 :(得分:0)

如果你以稍微不同的方式看待它,那么这个重载功能就不难管理了。请考虑以下内容,

public Integer | String f(int choice){
if(choice==1){
return new string();
}else{
return new Integer();
}}

如果语言确实返回重载,它将允许参数重载,但不允许重复。 这样可以解决以下问题:

main (){
f(x)
}

因为只有一个f(int选项)可供选择。

答案 9 :(得分:0)

在.NET中,有时我们使用一个参数来指示通用结果的所需输出,然后进行转换以获得我们期望的结果。

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();
    }

也许这个例子也有帮助:

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;
}

答案 10 :(得分:0)

对于记录, Octave 根据返回元素是标量与数组允许不同的结果。

.menu:focus {
  outline-color: transparent;
}

同样也Singular Value Decomposition

答案 11 :(得分:0)

这个与C ++略有不同;我不知道是否会直接将返回类型视为重载。它更像是一种以。的方式行事的模板专业化。

<强> util.h

#ifndef UTIL_H
#define UTIL_H

#include <string>
#include <sstream>
#include <algorithm>

class util {
public: 
    static int      convertToInt( const std::string& str );
    static unsigned convertToUnsigned( const std::string& str );
    static float    convertToFloat( const std::string& str );
    static double   convertToDouble( const std::string& str );

private:
    util();
    util( const util& c );
    util& operator=( const util& c );

    template<typename T>
    static bool stringToValue( const std::string& str, T* pVal, unsigned numValues );

    template<typename T>
    static T getValue( const std::string& str, std::size_t& remainder );
};

#include "util.inl"

#endif UTIL_H

<强> util.inl

template<typename T>
static bool util::stringToValue( const std::string& str, T* pValue, unsigned numValues ) {
    int numCommas = std::count(str.begin(), str.end(), ',');
    if (numCommas != numValues - 1) {
        return false;
    }

    std::size_t remainder;
    pValue[0] = getValue<T>(str, remainder);

    if (numValues == 1) {
        if (str.size() != remainder) {
            return false;
        }
    }
    else {
        std::size_t offset = remainder;
        if (str.at(offset) != ',') {
            return false;
        }

        unsigned lastIdx = numValues - 1;
        for (unsigned u = 1; u < numValues; ++u) {
            pValue[u] = getValue<T>(str.substr(++offset), remainder);
            offset += remainder;
            if ((u < lastIdx && str.at(offset) != ',') ||
                (u == lastIdx && offset != str.size()))
            {
                return false;
            }
        }
    }
    return true;    
}

<强> util.cpp

#include "util.h"

template<>
int util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoi( str, &remainder );
} 

template<>
unsigned util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoul( str, &remainder );
}

template<>
float util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stof( str, &remainder );
}     

template<>   
double util::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stod( str, &remainder );
}

int util::convertToInt( const std::string& str ) {
    int i = 0;
    if ( !stringToValue( str, &i, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
        throw strStream.str();
    }
    return i;
}

unsigned util::convertToUnsigned( const std::string& str ) {
    unsigned u = 0;
    if ( !stringToValue( str, &u, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
        throw strStream.str();
    }
    return u;
}     

float util::convertToFloat(const std::string& str) {
    float f = 0;
    if (!stringToValue(str, &f, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
        throw strStream.str();
    }
    return f;
}

double util::convertToDouble(const std::string& str) {
    float d = 0;
    if (!stringToValue(str, &d, 1)) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to double";
        throw strStream.str();
    }
    return d;
}

此示例并不完全使用返回类型的函数重载解析,但是此c ++非对象类使用模板特化来通过返回类型使用私有静态方法来模拟函数重载解析。

每个convertToType函数都在调用函数模板stringToValue(),如果你看一下这个函数模板的实现细节或算法,它就会调用getValue<T>( param, param )并返回一个键入T并将其存储到作为其参数之一传递到T*函数模板的stringToValue()中。

除此之外; C ++实际上没有一种机制可以通过返回类型来重载函数。可能还有其他我不知道的构造或机制可以通过返回类型来模拟解析。

答案 12 :(得分:-1)

我认为这是现代C ++定义中的GAP ......为什么?

int func();
double func();

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

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

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

为什么C ++编译器不能在示例中引发错误&#34; 3&#34;和 接受示例中的代码&#34; 1 + 2&#34; ??

答案 13 :(得分:-2)

大多数静态语言现在也支持泛型,这可以解决您的问题。如前所述,没有参数差异,就无法知道要调用哪一个。因此,如果你想这样做,只需使用泛型并称之为一天。