【问题标题】:Function overloading -- two functions only differ by a default parameter函数重载——两个函数只有一个默认参数不同
【发布时间】:2020-11-14 03:32:42
【问题描述】:
class A{
    public:
        void foo(int x)
        {
            cout << "foo with one\n";
        }

        void foo(int x, int y=10)
        {
            cout << "foo with two\n";
        }
};

int main()
{
    A a;
    a.foo(1);   //error?
}

那么,为什么我不能用带默认参数的函数重载void foo(int)

【问题讨论】:

  • 在 foo(10) 的情况下应该调用哪个函数?如果你回答这个问题,你会发现你的解决方案是多余的。
  • 您为什么要这样做?你真的有两个不同的函数可以用相同的参数调用并执行不同的任务吗?如果您需要两种不同的实现,请考虑提供不同的名称,其他开发人员会很高兴通过名称来识别他们在做什么......
  • @DavidRodríguez-dribeas,实际上没有。但这只是我的想法,:-)

标签: c++ overloading


【解决方案1】:

不,您不能根据传递的参数的 重载函数,因此也不允许根据默认参数的值进行重载。

您只能基于以下条件重载函数:

  • 参数类型
  • 参数数量
  • 参数序列 &
  • constvolatile 等限定符。

当然,编译器是否接受重载取决于以下事实:
编译器是否明确解析函数的调用。

在您的情况下,编译器无法解决歧义,例如:如果您简单地将函数调用为:编译器将不知道要调用哪个重载函数:

 foo(100);

编译器无法做出决定,因此出现错误。

【讨论】:

  • 所以,我不能用另一个函数重载一个函数,它与第一个函数的不同之处在于默认 arg?
  • @Alcott:不,你不能,编译器将如何决定调用哪个函数?编译器无法确定唯一的区别是否是默认参数。
  • 有趣的是,您的第二段与您的第一段相矛盾,因为参数的数量不同。
  • @LuchianGrigore:是的有趣好吧。看起来你玩得很开心。
  • 好吧,但是因为是星期五,这总是一件好事。
【解决方案2】:

不可以,使用单个参数调用函数时会产生歧义。

如果你需要这样做,那就是代码异味。

【讨论】:

    【解决方案3】:

    我确实推荐这个,但你可以定义这种模棱两可的方法并通过不同的接口使用它们。 (以下至少适用于使用 -std=c++11 的 gcc 4.8.0。)

    考虑两个接口:

    class IInterface1
    {
    public:
       virtual void funky(int i) = 0;
       virtual void funky(int i, int j) = 0;
    };
    
    class IInterface2
    {
    public:
       virtual void funky(int i, int j = 0) = 0;
    };
    

    IInterface1 为不同的参数重载了两次 funky 方法,即相同的方法名称,但一个采用单个 int,而另一个采用两个 int。注意在接口实现中,funky 方法需要有两个实现(一个用于一个参数,另一个用于两个参数)。

    IInterface2 有一个 funky 方法,在调用时采用一个或两个整数。如果未明确提供,则第二个 int 为默认值。请注意,在接口实现中,funky 方法只需要 一个 实现(并且无论在调用期间提供一个还是两个参数,它总是需要两个参数)。

    实现两个接口的类:

    class Foo : public IInterface1, public IInterface2
    {
    public:
       void funky(int i) override
          { printf("  funky(int i) -> funky(%d)\n", i); }
       void funky(int i, int j = 0) override
          { printf("  funky(int i, int j = 0) -> funky(%d, %d)\n", i, j); }
       void funky(int i, int j = 0, int k = 0)
          { printf("  funky(int i, int j = 0, int k = 0) -> funky(%d, %d, %d)\n", i, j, k); }
    };
    

    为了说明,Foo 还添加了funky 方法的第三个重载版本,一个接受三个参数(一个强制,两个可选)。

    Foo 现在可以如下图所示使用。 Foomaster的实例可以直接使用,也可以让不同的客户端访问master对象的不同接口。

    Foo master;
    IInterface1& client1 = master;
    IInterface2& client2 = master;
    
    // AMBIGUOUS: master.funky(1); 
    // AMBIGUOUS: master.funky(2,3);
    puts("master.funky(4, 5, 6);");
    master.funky(4, 5, 6);
    
    puts("client1.funky(7);");
    client1.funky(7);
    puts("client1.funky(8, 9);");
    client1.funky(8, 9);
    
    puts("client2.funky(10);");
    client2.funky(10);
    puts("client2.funky(11, 12);");
    client2.funky(11, 12);
    

    这将产生以下输出:

    master.funky(4, 5, 6);
      funky(int i, int j = 0, int k = 0) -> funky(4, 5, 6)
    client1.funky(7);
      funky(int i) -> funky(7)
    client1.funky(8, 9);
      funky(int i, int j = 0) -> funky(8, 9)
    client2.funky(10);
      funky(int i, int j = 0) -> funky(10, 0)
    client2.funky(11, 12);
      funky(int i, int j = 0) -> funky(11, 12)
    

    总而言之,类可能具有明显冲突的方法重载版本。在调用方法时必须解决歧义(否则代码将无法编译)。

    PS:同样,由于上述做法违反了KISS原则,我不宽恕它。

    【讨论】:

      【解决方案4】:

      为什么不呢?

      class A{
          public:
      
              void foo(int x=10, int y=10)
              {
                  cout << "foo with two\n";
              }
      };
      

      【讨论】:

        【解决方案5】:

        想一想 - 在编译时编译器必须决定选择哪一个。除非您提供两个参数,否则我不能。所以编译器别无选择,只能举手说再试一次,代码不需要 Mystic Meg。

        【讨论】:

          【解决方案6】:

          我认为你不能。因为函数/运算符重载由编译器在编译时解决。因此,仅通过提供默认参数来重载函数会导致歧义和编译器错误。

          【讨论】:

            【解决方案7】:

            这种歧义不会因为可选参数而发生,但只有当你有相同类型的原型具有相同的可选参数但名称不同时才会发生......这就是我想出来的......

            实际上,当我的示例与您的示例相似时,我真的认为我很幸运。但是当我将 2 个具有相同可选参数的不同原型名称放在一起时,它开始抱怨模棱两可

            下面给出了歧义错误

            void UpdateTextColor(HWND hwndControl, DWORD text_color, DWORD back_color, BOOL control_back_color);
            void CreateColoredText(HWND hwndControl, DWORD text_color, DWORD back_color, BOOL control_back_color);
            
            void UpdateTextColor(HWND hwndControl, DWORD text_color, DWORD back_color, BOOL control_back_color = FALSE)
            {
               //...
            }
            
            void CreateColoredText(HWND hwndControl, DWORD text_color, DWORD back_color, BOOL control_back_color = FALSE)
            {
               //...
            }
            

            但这并没有,请注意我仍在使用可选参数

            void UpdateTextColor(HWND hwndControl, DWORD text_color, DWORD back_color);
            void CreateColoredText(HWND hwndControl, DWORD text_color, DWORD back_color, BOOL control_back_color);
            
            void UpdateTextColor(HWND hwndControl, DWORD text_color, DWORD back_color)
            {
               //...
            }
            
            void CreateColoredText(HWND hwndControl, DWORD text_color, DWORD back_color, BOOL control_back_color = FALSE)
            {
               //...
            }
            
            //My call is like this (note only 3 params used out of 4)
            
            CreateColoredText(GetDlgItem(hDlg, BLAHBLAH), RGB(0, 0, 0), RGB(127, 127, 127));
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-12-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-10-10
              • 1970-01-01
              相关资源
              最近更新 更多