按返回类型重载
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】:
这是可能的,但我不确定这是我推荐的技术
初学者。与其他情况一样,当您希望选择功能时
取决于如何使用返回值,您使用代理;首先定义
像getChar
和getInt
这样的函数,然后是一个通用的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&
与 this&&
)。【参考方案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_int
和get_char
将是您能做的最好的。
【讨论】:
只是为了确定:像template <class T> T get(int)
这样的东西会起作用吗?
是的,@Niklas,但您必须将其称为 get<int>
或 get<char>
,如果您是 get_int
和 get_char
也不使用其他模板功能。
@Rob:好吧,如果你有类似T get(T)
的东西,编译器可以确定T
。如果您调用get('a')
,编译器会推断出T
是char
,您不必显式调用get<char>('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;
完美运行!
【讨论】:
以上是关于按返回类型重载的主要内容,如果未能解决你的问题,请参考以下文章