按返回类型重载


80

我在这里读了一些关于此主题的问题,这似乎令我感到困惑。我刚刚开始学习C ++,还没有研究模板或运算符重载等。

现在有一种简单的过载方法

class My {
public:
    int get(int);
    char get(int);
}

没有模板或奇怪的行为?还是我应该

class My {
public:
    int get_int(int);
    char get_char(int);
}



1
@AdamV,我真的很喜欢你的评论。短但完全牢固。
2014年

@Adam V实际上,使用重载函数的地址已经很含糊了。在这种情况下,应该对表达式有某种类型的期望。如果没有一个,则程序格式错误。并且这已经实现。我认为使用相同的规则通过返回类型实现函数重载不会很困难。因此,在您的具体示例中,将使用返回类型的强制类型转换消除歧义。与实例化int返回值将是这样的(int)get(9),用char这样的(char)get(9)
AnArrayOfFunctions 2015年

当您到达此处时,我认为最好的选择是考虑两个不同的函数名称,如Luchian所建议的那样。
Kemin Zhou

Answers:


98

不,没有。您不能基于返回类型重载方法。

重载解析考虑了功能签名。函数签名由以下内容组成:

  • 功能名称
  • 简历限定词
  • 参数类型

这是报价:

1.3.11签名

有关参与重载解析的函数的信息(13.3):其参数类型列表(8.3.5),如果该函数是类成员,则该函数本身和该类上的cv限定词(如果有)在其中声明成员函数。[...]

选项:

1)更改方法名称:

class My {
public:
    int getInt(int);
    char getChar(int);
};

2)out参数:

class My {
public:
    void get(int, int&);
    void get(int, char&);
}

3)模板...在这种情况下过大了。


17
您不能在返回类型上重载普通的函数,但是编译器将根据结果类型在转换运算符之间进行选择。您可以利用它来创建一个代理,该代理可以有效地发挥作用,就好像您在返回类型上超载了一样。
James Kanze 2012年

2
@JeffPigarelli模板解决方案表示成员模板:My::get<T>(int)。这是一个有效的替代方法_if 1),您必须处理许多不同的类型,并且都使用相同的基本代码(例如boost::lexical_cast<T>( someStringValue ),或者必须能够从其他模板调用这些函数(myMy.get<T>( i ),这T是另一个模板的参数) –否则,如Luchian所说,他们是过大杀手
James Kanze 2012年

39
请注意,不能基于返回类型重载的原因是C ++允许您丢弃函数调用的值。因此,如果您只是简单地调用my.get(0);编译器,则将无法决定执行哪段代码。
benzado 2012年

9
在这种情况下@benzado,它应该只在这样的情况下抛出编译器错误,否则像它在做它应该推断类型这么多其他方案了。
rr-

2
出于同样的原因,void foo(int x = 0) {} void foo(double x = 0) {}不应使用@benzado 。但是,事实并非如此。仅在编译器确实无法区分(foo())的情况下,您才会得到错误
idclev 463035818

82

有可能,但是我不确定这是我向初学者推荐的技术。与其他情况一样,当您希望函数的选择取决于返回值的使用方式时,可以使用代理。首先定义类似getChar和的函数getInt,然后定义一个泛型get(),该泛型返回如下所示的Proxy:

class Proxy
{
    My const* myOwner;
public:
    Proxy( My const* owner ) : myOwner( owner ) {}
    operator int() const
    {
        return myOwner->getInt();
    }
    operator char() const
    {
        return myOwner->getChar();
    }
};

根据需要将其扩展为多种类型。


10
+1(虽然是一个极端情况),但转换运算符实际上在返回类型上重载,并且可以利用该运算符在任何地方获得此功能。
Matthieu M.

3
@MatthieuM。随处可见,但通常会涉及隐式转换的注意事项。您确实冒着引入歧义的风险,否则就不会存在歧义。但是,对于代理服务器,我认为风险很小-除非需要隐式转换,否则您将没有代理服务器类型的实例。还要注意,代理中的转换算作一个用户定义的转换。如果您需要std::string,而代理仅提供operator char const*(),则它将无法正常工作。
James Kanze 2012年

为什么在这里使用代理,我想不出必须使用代理的任何情况。你能提供一个吗?谢谢!
陈力2011年

8

不,您不能按返回类型重载;仅按参数类型和const / volatile限定符。

一种替代方法是使用引用参数“返回”:

void get(int, int&);
void get(int, char&);

尽管我可能会使用模板,也可能会使用名称不同的函数,例如您的第二个示例。


EFI API,其中返回类型为int错误代码。
科尔·约翰逊

1
注意,char和int类型可以隐式转换。
Kemin Zhou

5

您可以这样想:

你有:

  int get(int);
  char get(int);

并且,在调用时不强制收集函数的返回值。

现在,您调用

  get(10);  -> there is an ambiguity here which function to invoke. 

因此,如果根据返回类型允许重载,则没有意义。


4

恢复旧线程,但我看不到有人提到ref限定符超载。引用限定词是C ++ 11中添加的一种语言功能,我最近才偶然发现它-它不像cv限定词那样广泛。主要思想是区分两种情况:何时在右值对象上调用成员函数,以及何时在左值对象上调用成员函数。您基本上可以编写如下代码(我在稍微修改OP的代码):

#include <stdio.h>

class My {
public:
    int get(int) & { // notice &
        printf("returning int..\n");
        return 42;
    }
    char get(int) && { // notice &&
        printf("returning char..\n");
        return 'x';
    };
};

int main() {
    My oh_my;
    oh_my.get(13); // 'oh_my' is an lvalue
    My().get(13); // 'My()' is a temporary, i.e. an rvalue
}

此代码将产生以下输出:

returning int..
returning char..

当然,与cv-qualifiers一样,这两个函数都可能返回相同的类型,并且重载仍将成功。


3

尽管有关此问题的大多数其他注释在技术上都是正确的,但如果将返回值与重载输入参数结合使用,则可以有效地重载返回值。例如:

class My {
public:
    int  get(int);
    char get(unsigned int);
};

演示:

#include <stdio.h>

class My {
public:
    int  get(         int x) { return 'I';  };
    char get(unsinged int x) { return 'C';  };
};

int main() {

    int i;
    My test;

    printf( "%c\n", test.get(               i) );
    printf( "%c\n", test.get((unsigned int) i) );
}

结果是:

I 
C

6
完全改变了函数签名,因此您不会按返回类型进行重载,而只是重载
Dado

我成功使用此方法为运行生产的C ++ JSON API返回了各种值。很棒!尽管从技术上讲不会按返回类型重载,但它可以使用相同的函数名来实现不同返回类型的意图,并且是有效且清晰的C ++,并且开销很小(函数调用中的单个变量实例化)。
guidotex

尽管这不会因返回类型而超载,但它可以完成工作。让我说“偷偷摸摸”
Marcus

3

如前所述,在这种情况下,模板是过大的,但是仍然值得一提。

class My {
public:
    template<typename T> T get(int);
};

template<> int My::get<int>(int);
template<> char My::get<char>(int);

2

在C ++中,无法通过返回类型来重载。如果不使用模板,则使用get_intget_char将是您的最佳选择。


只是要确定:类似的东西template <class T> T get(int)行得通吗?
Niklas B.

4
是的,@Niklas,但你不得不称其为get<int>或者get<char>,它并没有真正得到你太多了get_intget_char如果你不也用其他的模板功能。
罗伯·肯尼迪

@Rob:好吧,编译器可以确定T您是否有类似的东西T get(T)。如果您调用get('a'),编译器将推导Ta,char而不必显式调用get<char>('a')。我仍然不确定这是否是标准的,尽管我认为是标准的。仅供参考,GCC和Clang都支持。
netcoder 2012年

1
这是@Netcoder的完美标准,但不是编译器仅推断出返回类型的情况,您建议这样做是可能的。在您的示例中,编译器推断出参数类型,并且一旦知道该类型,便会填充T其他所有位置的值,包括返回类型。我希望您能T在Niklas的第一句话中举例说明该函数的编译器。
罗伯·肯尼迪

2

您不能基于返回类型重载方法。最好的选择是创建两个语法稍有不同的函数,例如在第二个代码段中。


0

您不能基于函数的返回类型重载函数。您可以根据此函数采用的参数的类型和数量进行超前处理。


0

我使用代理人使用James Kanze的答案:

https://stackoverflow.com/a/9569120/262458

我想避免在void *上使用大量难看的static_casts,所以我这样做:

#include <SDL_joystick.h>
#include <SDL_gamecontroller.h>

struct JoyDev {
    private:
        union {
            SDL_GameController* dev_gc = nullptr;
            SDL_Joystick*       dev_js;
        };
    public:
        operator SDL_GameController*&() { return dev_gc; }
        operator SDL_Joystick*&()       { return dev_js; }

        SDL_GameController*& operator=(SDL_GameController* p) { dev_gc = p; return dev_gc; }
        SDL_Joystick*&       operator=(SDL_Joystick* p)       { dev_js = p; return dev_js; }
};

struct JoyState {
    public:
        JoyDev dev;
};

int main(int argc, char** argv)
{
    JoyState js;

    js.dev = SDL_JoystickOpen(0);

    js.dev = SDL_GameControllerOpen(0);

    SDL_GameControllerRumble(js.dev, 0xFFFF, 0xFFFF, 300);

    return 0;
}

完美的作品!

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.