函数是否按返回类型重载?


Answers:


523

与其他人所说的相反,按返回类型重载可能的,并且由某些现代语言完成的。通常的反对意见是在类似

int func();
string func();
int main() { func(); }

您无法分辨func()正在调用哪个。这可以通过几种方法解决:

  1. 有一种可预测的方法来确定在这种情况下调用哪个函数。
  2. 每当发生这种情况时,就是编译时错误。但是,具有允许程序员消除歧义的语法,例如int main() { (string)func(); }
  3. 没有副作用。如果您没有副作用,并且从不使用函数的返回值,那么编译器可以避免首先调用该函数。

语言我会定期(两个AB:)使用过载返回类型的Perl哈斯克尔。让我描述一下他们的工作。

Perl中标量列表上下文(以及其他上下文)之间有根本的区别,但我们会假装有两个。Perl中的每个内置函数都可以执行不同的操作,具体取决于调用它的上下文。例如,join操作员强制列表上下文(在连接的事物上),而scalar操作员强制标量上下文,因此比较:

print join " ", localtime(); # printed "58 11 2 14 0 109 3 13 0" for me right now
print scalar localtime(); # printed "Wed Jan 14 02:12:44 2009" for me right now.

Perl中的每个运算符都在标量上下文中执行某些操作,而在列表上下文中执行某些操作,如图所示,它们可能有所不同。(这不仅适用于像这样的随机运算符localtime。如果@a在列表上下文中使用数组,它将返回该数组,而在标量上下文中,它将返回元素的数量。因此,例如,print @a打印出元素,而print 0+@a打印出大小。 )此外,每个运算符都可以强制使用上下文,例如加法+强制使用标量上下文。man perlfunc文件中的每个条目都对此进行了记录。例如,这是条目的一部分glob EXPR

在列表上下文中,返回值(EXPR如标准Unix Shell /bin/csh会执行的操作)的文件名扩展列表(可能为空)。在标量上下文中,glob遍历此类文件名扩展,当列表用尽时返回undef。

现在,列表和标量上下文之间是什么关系?好吧,man perlfunc

请记住以下重要规则:没有规则将表达式在列表上下文中的行为与其在标量上下文中的行为相关联,反之亦然。它可能会做两件完全不同的事情。每个运算符和函数都决定最适合在标量上下文中返回哪种值。一些运算符返回在列表上下文中应返回的列表长度。一些运算符返回列表中的第一个值。一些运算符返回列表中的最后一个值。一些操作员返回成功操作的计数。通常,除非您需要一致性,否则它们会执行您想要的操作。

因此,拥有一个函数并不是一件简单的事,然后最后进行简单的转换。实际上,localtime由于这个原因,我选择了示例。

具有此行为的不仅仅是内置组件。任何用户都可以使用定义此类函数wantarray,该函数可让您区分列表,标量和void上下文。因此,例如,如果您在空白上下文中被调用,则可以决定不执行任何操作。

现在,您可能会抱怨返回值不是真正的重载,因为您只有一个函数,该函数会在调用上下文时被告知,然后根据该信息进行操作。但是,这显然是等效的(类似于Perl不允许字面上通常进行的重载,但是函数只能检查其参数)。此外,它很好地解决了此响应开始时提到的模棱两可的情况。Perl不会抱怨它不知道调用哪种方法。它只是称之为。它要做的就是找出调用该函数的上下文,这始终是可能的:

sub func {
    if( not defined wantarray ) {
        print "void\n";
    } elsif( wantarray ) {
        print "list\n";
    } else {
        print "scalar\n";
    }
}

func(); # prints "void"
() = func(); # prints "list"
0+func(); # prints "scalar"

(注意:当我指的是函数时,我有时可能会说Perl运算符。这对本次讨论并不重要。)

Haskell采用另一种方法,即没有副作用。它还具有强大的类型系统,因此您可以编写如下代码:

main = do n <- readLn
          print (sqrt n) -- note that this is aligned below the n, if you care to run this

此代码从标准输入读取浮点数,并打印其平方根。但是,对此有何惊奇?好吧,类型readLnreadLn :: Read a => IO a。这意味着对于任何可能的类型Read(形式上是Read类型类实例的每个类型),readLn都可以读取它。Haskell如何知道我想读取浮点数?好的,类型sqrtsqrt :: Floating a => a -> a,这实际上意味着sqrt只能接受浮点数作为输入,因此Haskell推断了我想要的内容。

Haskell无法推断我想要什么会发生什么?好吧,有几种可能性。如果我根本不使用返回值,Haskell根本不会首先调用该函数。但是,如果我确实使用了返回值,那么Haskell将会抱怨它无法推断类型:

main = do n <- readLn
          print n
-- this program results in a compile-time error "Unresolved top-level overloading"

我可以通过指定所需的类型来解决歧义:

main = do n <- readLn
          print (n::Int)
-- this compiles (and does what I want)

无论如何,整个讨论意味着通过返回值进行重载是可能的并且已经完成,这回答了您的部分问题。

问题的另一部分是为什么更多的语言不这样做。我让别人回答。但是,有几点评论:原则上的原因可能是,与参数类型的重载相比,混淆的机会确实更大。您还可以查看各种语言的基本原理:

Ada:“最简单的重载解决方案规则似乎是使用一切-尽可能从尽可能广泛的上下文中获取所有信息-来解析重载引用。此规则可能很简单,但无济于事。它需要人类阅读者扫描任意大块的文本并做出任意复杂的推断(例如上面的(g))。我们认为,更好的规则是明确规定人类阅读器或编译器必须执行的任务,并使该任务对人类读者来说,这是自然而然的事情。”

C ++(Bjarne Stroustrup的“ C ++编程语言”的7.4.1小节):“重载解析中不考虑返回类型。原因是保持解析度独立于单个运算符或函数调用,与上下文无关。请考虑:

float sqrt(float);
double sqrt(double);

void f(double da, float fla)
{
    float fl = sqrt(da);     // call sqrt(double)
    double d = sqrt(da); // call sqrt(double)
    fl = sqrt(fla);            // call sqrt(float)
    d = sqrt(fla);             // call sqrt(float)
}

如果考虑到返回类型,将不再可能单独查看sqrt()in 的调用并确定调用了哪个函数。”(请注意,为了进行比较,在Haskell中没有隐式转换。)

Java(Java语言规范9.4.1):“其中一个继承的方法必须可以对其他所有继承的方法进行返回类型替换,否则将发生编译时错误。” (是的,我知道这并没有给出基本原理。我确定基本原理是Gosling在“ Java编程语言”中给出的。也许有人抄袭了吗?我敢打赌,这实质上是“最不令人惊讶的原则”。 )但是,关于Java的一个有趣的事实是:JVM 允许通过返回值进行重载!例如,它在Scala中使用,并且可以通过内部玩法直接通过Java访问。

PS。最后一点,实际上可以通过技巧在C ++中通过返回值重载。证人:

struct func {
    operator string() { return "1";}
    operator int() { return 2; }
};

int main( ) {
    int x    = func(); // calls int version
    string y = func(); // calls string version
    double d = func(); // calls int version
    cout << func() << endl; // calls int version
    func(); // calls neither
}

很棒的帖子,但是您可能想弄清楚什么是读物(字符串->某种东西)。
Thomas Eding

C ++还让您按const / not const返回值重载。stackoverflow.com/questions/251159/...

3
对于重载强制操作符的最后一个技巧,有时有时会使用“ cout”行,但是我对代码进行的几乎任何更改都使它给出“'operator <<'的模棱两可的重载”。
史蒂夫

1
我赞成的方法是要求将一个重载标记为“首选”。编译器将从仅使用首选重载进行绑定开始,然后确定是否有任何非首选重载会有所改善。其中,假设类型FooBar支持双向转换,并且一种方法在Foo内部使用type 但返回type Bar。如果代码调用了这样的方法,该方法将立即将结果强制为type Foo,则可以使用Barreturn类型,但是Foo这样做会更好。顺便说一句,我也想看看......
supercat 2012年

...一种方法可以指定在类似的构造中应使用什么类型var someVar = someMethod();(或者指定不应以这种方式使用其返回值)。例如,实现Fluent接口的一类类型可能会受益于具有可变和不可变的版本,因此var thing2 = thing1.WithX(3).WithY(5).WithZ(9);WithX(3)复制thing1到可变对象,对X进行突变并返回该可变对象。WithY(5)会使Y变异并返回相同的对象;同样是WithZ(9)。然后,分配将转换为不可变的类型。
supercat 2012年

37

如果函数由返回类型重载,而您有这两个重载

int func();
string func();

看到这样的调用,编译器无法找出要调用的两个函数中的哪个

void main() 
{
    func();
}

因此,语言设计人员通常不允许返回值重载。

但是,某些语言(例如MSIL)确实允许按返回类型进行重载。他们当然也面临上述困难,但是他们有变通办法,您必须查阅其文档。


4
一个小问题(您的答案给出了一个非常清晰,可以理解的理由):并非没有办法;只是这些方法笨拙,比大多数人想要的更痛苦。例如,在C ++中,使用某些丑陋的强制转换语法可以解决超载。
迈克尔·伯

2
@JörgW Mittag:您看不到功能的作用。它们很容易产生不同的副作用。
A. Rex

2
@Jörg-在大多数主流编程语言(C / C ++,C#,Java等)中,功能通常都有副作用。实际上,我想带有副作用的功能至少和没有副作用的功能一样普遍。
迈克尔·伯尔

6
跳到这里晚了,但是在某些情况下,“功能”的定义狭窄(基本上)是“一种没有副作用的方法”。通俗地说,“功能”通常与“方法”或“子程序”互换使用。根据您的观点
豪尔赫

3
跳入更晚的时候,某些观点可能会使用严格或学究以外的形容词
Patrick McDonald

27

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

f(g(x))

如果f有过载void f(int)void f(string)并且g有过载int g(int)string g(int)?您将需要某种歧义消除器。

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


2
常规的过载也可能导致歧义。我认为通常可以通过计算所需的强制转换次数来解决这些问题,但这并不总是可行的。
杰·康罗德

1
是的,标准转化分为完全匹配,升级和转化:void f(int); 无效f(long); F A'); 调用f(int),因为这只是升级,而转换为long则是转换。无效f(float); 无效f(short); f(10);都需要转换:通话不明确。
Johannes Schaub-litb

如果该语言的评估比较懒惰,那么这并不是什么大问题。
jdd 2010年

否决,Rex的帖子中未解决参数类型重载和返回类型重载的交互。很好。
约瑟夫·加文

1
在设计一种语言时,我的规则是,对于任何重载函数,每个参数签名都必须具有一个指定为默认值的返回类型。假设每个函数调用将使用默认类型,则编译器将开始。但是,一旦完成,在每种情况下,函数的返回值都将立即转换为或强制转换为其他值,则编译器将检查参数签名相同但其返回类型更匹配(或可能为空)的重载。 。对于这样的重载,我可能还会强加一个“覆盖所有的覆盖”规则。
supercat 2012年

19

要从另一个非常相似的问题(重复?)窃取C ++特定的答案


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

原因是保持解析度独立于单个运算符或函数调用。

他们希望它仅基于重载的调用方式,而不是基于结果的使用方式(如果完全使用了结果)。实际上,在不使用结果的情况下调用了许多函数,或者将结果用作较大表达式的一部分。我确定他们决定这样做的一个因素是,如果返回类型是解决方案的一部分,那么将会有很多对重载函数的调用,这些调用需要使用复杂的规则来解决,或者必须让编译器抛出通话不明确的错误。

而且,Lord知道,C ++重载解析足够复杂了……


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,类型推断,编译)
  • 集成部署工具
  • 图书馆支持
  • 社区支持和聚会场所
  • 丰富的标准库
  • 良好的语法
  • 读取评估打印循环
  • 支持反射式编程

3
Haskell超载。类型类是用于定义重载函数的语言功能。
Lii 2012年

2

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

而我想要写的...

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都属于解决这种C ++难题的方式的最优途径:)


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;

那是一个可怕的主意。不要引入伪参数,这是一个很大的代码味道。相反,请选择其他名称,或者选择可以像这样的返回类型,或者是受歧视的联合或类似内容。
亚伯

@Abel您的建议实际上是一个糟糕的主意,因为整个主意是关于此虚拟参数的,并且这样命名,以使开发人员清楚此参数是虚拟参数,即使在您不知道用默认值伪参数在许多图书馆使用,VCL在Delphi中,和许多IDE,例如德尔福,你可以看到它在SafeLoadLibrary的sysutils的单位...
ZORRO_BLANCO

当然,在某些情况下,伪参数很有用,例如在map或fold操作中的lambda中,或者在实现接口时。但是,仅出于造成超载的目的,不,我不同意。没必要,没有程序员,这就是噪音。
亚伯

0

如前所述,仅返回类型不同的函数的歧义调用会引起歧义。歧义导致缺陷代码。必须避免有缺陷的代码。

模棱两可的尝试导致的复杂性表明,这不是一个好办法。除了进行智力练习外,为什么不使用带有参考参数的程序。

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

因为您不能轻易地立即重用“返回”值。您必须在单个线路上进行每次通话,而不是doing(thisVery(deeplyNested(), andOften(butNotAlways()), notReally()), goodCode());
Adowrath

0

如果您以稍微不同的方式查看它,则不难管理此重载功能。考虑以下,

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

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

main (){
f(x)
}

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


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

1
这是一种小技巧,如果用户没有正确地转换结果,或者开发人员没有将返回类型与枚举正确匹配,则可能导致运行时错误。我建议使用基于模板的方法(或C#中的通用参数?),例如此答案
sleblanc

0

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

x = min ([1, 3, 0, 2, 0])
   ⇒  x = 0

[x, ix] = min ([1, 3, 0, 2, 0])
   ⇒  x = 0
      ix = 3 (item index)

Cf也有奇异值分解


0

对于C ++,这一点略有不同。我不知道返回类型是否会将其直接视为过载。更多的是模板专门化以这种方式起作用。

实用程序

#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

实用程序

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

实用程序

#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 ++实际上并没有一种机制可以通过返回类型来实现函数重载解析。我可能不知道还有其他构造或机制可以按返回类型模拟解析。


-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 ++编译器不能在示例“ 3”中引发错误并不能在示例“ 1 + 2”中接受代码?


是的,这就是当时他们考虑使用C#(也许是C ++)的原因。但是,尽管您的代码很琐碎,但是一旦添加了类层次结构,虚拟方法,抽象和接口,其他重载以及有时是多重继承,决定要解决的方法就会变得非常复杂。设计师可以选择不走这条路,但是其他语言在不同的成功水平上都做出了不同的决定。
亚伯

-2

现在,大多数静态语言还支持泛型,这将解决您的问题。如前所述,没有参数diffs,就无法知道要调用哪个参数。因此,如果要执行此操作,只需使用泛型并将其命名为一天。


不一样的东西。根据返回类型的使用方式,如何处理将输入转换为整数,浮点型,布尔型或其他类型的函数?不能一概而论,因为每种情况都需要特殊情况。
杰·康罗德

请参阅codeproject.com/KB/cpp/returnoverload.aspx,以获取“返回类型上的过载”的巧妙策略。基本上,不是定义函数func(),而是定义一个struct func,为它提供一个operator()()并转换为每种合适的类型。
j_random_hacker

Jay,您在调用函数时定义了返回类型。如果输注不同,则完全没有问题。如果存在相同的内容,则可以使用GetType()基于类型的通用版本。
查尔斯·格雷厄姆
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.