【问题标题】:Function Overloading Based on Value vs. Const Reference基于值与常量引用的函数重载
【发布时间】:2011-07-24 19:56:42
【问题描述】:

是否声明如下内容

void foo(int x)        { std::cout << "foo(int)"         << std::endl; }
void foo(const int &x) { std::cout << "foo(const int &)" << std::endl; }

有意义吗?调用者如何区分它们?我试过了

foo(9);  // Compiler complains ambiguous call.

int x = 9;
foo(x);  // Also ambiguous.

const int &y = x;
foo(y);  // Also ambiguous.

【问题讨论】:

    标签: c++ pass-by-reference pass-by-value overloading


    【解决方案1】:

    其意图似乎是区分临时调用(即9)和“常规”参数传递。第一种情况可能允许函数实现采用优化,因为很明显参数将在之后处理(这对于整数文字绝对毫无意义,但对于用户定义的对象可能有意义)。

    但是,当前的 C++ 语言标准没有提供一种专门针对参数的“左/右值”重载的方法——任何作为参数传递给函数的左值都可以隐式转换为引用,因此歧义是不可避免的。

    C++11 引入了一个类似目的的新工具——使用右值引用,你可以像下面这样重载

    void foo(int x)        { ... }
    void foo(const int &&x) { ... }
    

    ... 和 foo(4)(作为参数传递的临时 r 值)会导致编译器选择第二个重载,而 int i = 2; foo(i) 会选择第一个。

    注意:即使使用新工具链,也无法区分示例中的案例 2 和案例 3!)

    【讨论】:

    • 这不是真的,它们是可以区分的,请参阅@Inverse 的回答和这个重复问题的answer
    • 我使用 vs2012 进行了测试,但它仍然抱怨调用不明确,您确定转换为 const 引用有效吗?
    • const int &amp;&amp; 对于右值仍然无法与int 区分开来。
    【解决方案2】:

    你可以用模板做到这一点:

    template&lt;typename T&gt; void foo(T x) { ... }

    那么你可以通过值或引用来调用这个模板:

    int x = 123;
    foo<int>(x);  // by value
    foo<int const&>(x);  // by refernce
    

    【讨论】:

      【解决方案3】:

      调用者如何区分它们?

      在这种情况下无法区分。两个重载函数都具有与参数相同的原始数据类型type。而且参考引用不计入不同类型。

      【讨论】:

      【解决方案4】:

      您可以使用static_cast 显式选择要调用的重载:

      #include <iostream>
      
      void foo(int x)        { std::cout << "foo(int)"         << std::endl; }
      void foo(const int &x) { std::cout << "foo(const int &)" << std::endl; }
      
      int main()
      {
        int x = 0;
      
        auto f1 = static_cast< void(*)(int) >(foo);
        f1(x);
      
        auto f2 = static_cast< void(*)(const int&) >(foo);  
        f2(x);
      
      }
      

      但是,您应该问自己,为什么首先提供这两个重载。要么你可以制作副本,要么你不行。两者同时?为什么?还使调用者有必要显式选择重载破坏了函数重载的目的。如果你真的想考虑提供两个函数:

      void foo_copying(int x)        { std::cout << "foo(int)"         << std::endl; }
      void foo_non_copying(const int &x) { std::cout << "foo(const int &)" << std::endl; }
      

      【讨论】:

        【解决方案5】:

        不在 C++ 中。 Erlang 和 Haskell 等函数式语言通过允许您根据参数值指定函数重载而更加接近,但包括 C++ 在内的大多数命令式语言都需要基于方法签名的重载;即每个参数的个数和类型以及返回值的类型。

        签名中的const关键字定义的不是参数的类型,而是它在函数中的可变性; “const”参数如果被函数修改或通过引用传递给不使用const的任何函数,将产生编译器错误。

        【讨论】:

          【解决方案6】:

          编译器不能。 foo 的两种定义都可以用于 int 的所有“变体”。

          在第一个 foo 中,创建了 int 的副本。总是可以复制 int。

          在第二个 foo 中,传递了对 const int 的引用。由于任何 int 都可以转换为 const int,因此也可以传递对它的引用。

          由于两种变体在所有情况下都有效,因此编译器无法选择。

          如果你这样做,事情就会变得不同。使用以下定义:

          void foo (int &x);
          

          现在使用 foo(9) 调用它会采用第一种替代方法,因为您不能将 9 作为非常量 int 引用传递。

          另一个例子,如果你用一个拷贝构造函数是私有的类来替换 int,那么调用者就不能复制这个值,并且第一个 foo-variant 将不会被使用。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-04-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-07-24
            相关资源
            最近更新 更多