【问题标题】: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);
}

?

【问题讨论】:

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

标签: c++ overloading


【解决方案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://stackoverflow.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;
          }
          

          完美运行!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-07-29
            • 1970-01-01
            相关资源
            最近更新 更多