按返回类型重载

Posted

技术标签:

【中文标题】按返回类型重载【英文标题】:Overloading by return type 【发布时间】:2012-03-23 01:19:13 【问题描述】:

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

现在有没有简单的重载方法

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

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

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

?

【问题讨论】:

Function overloading by return type? 的可能重复项 @AdamV,我真的很喜欢你的评论。短但完全稳固。 @Adam V 实际上,获取重载函数的地址已经很模糊了。在这种情况下,应该对表达式有某种类型的期望。如果没有一个程序是格式错误的。这已经实施了。我认为使用相同的规则通过返回类型实现函数重载并不难。因此,在您的具体示例中,歧义将通过返回类型的强制转换来消除。使用int 实例化返回值将如下所示(int)get(9) 和使用char 就像这样(char)get(9) 当你到达这里时,我认为最好的选择是像 Luchian 建议的那样考虑两个不同的函数名称。 【参考方案1】:

不,没有。您不能根据返回类型重载方法。

重载解析考虑到函数签名。函数签名由以下部分组成:

函数名 cv 限定符 参数类型

这是引用:

1.3.11 签名

有关参与重载的函数的信息 分辨率(13.3):它的参数类型列表(8.3.5),如果 函数是类成员,函数上的 cv 限定符(如果有) 本身和声明成员函数的类。 [...]

选项:

1) 更改方法名称:

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

2) 输出参数:

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

3) 模板...在这种情况下有点矫枉过正。

【讨论】:

您不能在返回类型上重载普通函数,但编译器会根据结果类型在转换运算符之间进行选择;你可以利用它来创建一个代理,它的作用就像你在返回类型上重载一样。 @JeffPigarelli 模板解决方案是指会员模板:My::get<T>(int)。这是一个有效的替代方案_if 1) 你必须处理许多不同的类型,所有类型都具有相同的基本代码(例如boost::lexical_cast<T>( someStringValue ),或者你必须能够从其他模板调用这些函数(myMy.get<T>( i ),其中T 是另一个模板的参数。否则,正如 Luchian 所说,它们是矫枉过正。 请注意,您不能基于返回类型重载的原因是 C++ 允许您丢弃函数调用的值。因此,如果您只是简单地调用my.get(0);,编译器将无法决定执行哪段代码。 @benzado 在这种情况下,它应该只在这种情况下抛出编译器错误,否则它应该像在 so 许多其他场景中那样推断类型。 @benzado 出于同样的原因 void foo(int x = 0) void foo(double x = 0) 应该被禁止。然而,事实并非如此。只有在编译器真的无法区分的情况下(foo())才会报错【参考方案2】:

这是可能的,但我不确定这是我推荐的技术 初学者。与其他情况一样,当您希望选择功能时 取决于如何使用返回值,您使用代理;首先定义 像getChargetInt这样的函数,然后是一个通用的get() 返回这样的代理:

class Proxy

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

根据需要将其扩展到尽可能多的类型。

【讨论】:

+1,虽然是极端情况,但转换运算符实际上在返回类型上被重载,并且可以在任何地方利用此功能。 @MatthieuM。几乎无处不在,但有关于隐式转换的常见警告。你确实冒着引入歧义的风险,否则这些歧义是不会存在的。但是,在代理的情况下,我认为风险很小——除了需要隐式转换的情况外,您不会拥有代理类型的实例。另请注意,代理中的转化算作一次用户定义的转化。如果你需要std::string,而代理只提供operator char const*(),那就不行了。 为什么在这里使用代理,我想不出任何必须代理的情况。可以提供一份吗?谢谢!【参考方案3】:

不,你不能通过返回类型重载;只能通过参数类型和 const/volatile 限定符。

另一种选择是使用引用参数“返回”:

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

虽然我可能会使用模板,或者像您的第二个示例一样使用不同名称的函数。

【讨论】:

a la EFI API,其中返回类型是 int 错误代码。 注意,char和int类型可以隐式转换。 如果使用模板,您可能必须在使用时在尖括号中明确提供类型,对吧?因为在很多情况下,编译器无法知道您将其视为的确切类型? 嗯 fabda01 的答案似乎有解决方案,但我不明白它是如何工作的【参考方案4】:

如前所述,在这种情况下,模板是多余的,但它仍然是一个值得一提的选项。

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

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

【讨论】:

【参考方案5】:

你可以这样想:

你有:

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

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

现在,你调用

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

所以,根据返回类型是否允许重载是没有意义的。

【讨论】:

【参考方案6】:

复活一个旧线程,但我可以看到没有人提到 ref-qualifiers 的重载。 Ref-qualifiers 是 C++11 中添加的一种语言特性,我最近才偶然发现它——它并不像 e.g. 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 限定符一样,两个函数都可以返回相同的类型,并且重载仍然会成功。

【讨论】:

对于上下文,这里实际发生的是隐藏的this 参数上的正常函数重载(this&amp;this&amp;&amp;)。【参考方案7】:

虽然这个问题上的大多数其他 cmets 在技术上都是正确的,但您可以有效地重载返回值如果将它与重载输入参数结合使用。例如:

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

【讨论】:

这完全改变了函数签名,所以你不会被返回类型重载,你只是重载 我成功地使用此方法为运行生产的 C++ JSON API 返回了各种值。工作得很好!虽然从技术上讲不是通过返回类型重载,但它实现了具有相同函数名称的不同返回类型的意图,是有效且清晰的 C++,并且开销很小(函数调用中的单个变量实例化)。 虽然这不是通过返回类型重载,但它可以完成工作。让我说“偷偷摸摸”【参考方案8】:

在 C++ 中无法通过返回类型进行重载。在不使用模板的情况下,使用get_intget_char 将是您能做的最好的。

【讨论】:

只是为了确定:像template &lt;class T&gt; T get(int) 这样的东西会起作用吗? 是的,@Niklas,但您必须将其称为 get&lt;int&gt;get&lt;char&gt;,如果您是 get_intget_char也不使用其他模板功能。 @Rob:好吧,如果你有类似T get(T) 的东西,编译器可以确定T。如果您调用get('a'),编译器会推断出Tchar,您不必显式调用get&lt;char&gt;('a')。我仍然不确定这是否是标准的,尽管我认为它是标准的。仅供参考,GCC 和 Clang 都支持这个。 这是完全标准的,@Netcoder,但这不是编译器仅推断返回类型的情况,您建议这是可能的。在您的示例中,编译器推断出参数类型,一旦知道这一点,它就会在其他任何地方填充 T 的值,包括返回类型。我希望你能给出一个编译器为 Niklas 的第一条评论中的函数推导出 T 的例子。【参考方案9】:

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

【讨论】:

【参考方案10】:

您不能根据函数的返回类型重载函数。 您可以根据此函数采用的参数类型和数量进行覆盖。

【讨论】:

【参考方案11】:

我通过代理使用了 James Kanze 的答案:

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

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

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

完美运行!

【讨论】:

以上是关于按返回类型重载的主要内容,如果未能解决你的问题,请参考以下文章

重载中没有一个可以转换所有参数类型

如何按参数类型选择重载方法?

C++里面,为啥重载前++时不返回引用就不能连用?

c++中重载输出操作符,为啥要返回引用

为啥没有为不同的返回类型定义方法重载?

Java常见面试题02-方法重写和方法重载的区别?方法重载能改变返回值类型吗?