基于值与Const参考的函数重载

时间:2011-03-28 21:29:29

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

声明类似以下内容

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.

6 个答案:

答案 0 :(得分:10)

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

但是,当前的C ++语言标准没有提供专门为参数的'l / r-valueness'重载的方法 - 任何作为参数传递给函数的l值都可以隐式转换为引用,所以模棱两可是不可避免的。

C ++ 11为类似的目的引入了一个新工具 - 使用 r-value references ,你可以重载如下

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

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

note :即使使用新的工具链,也无法区分样本中的案例2和3!)

答案 1 :(得分:3)

  

调用者如何区分它们?

在这种情况下无法区分。重载函数都具有与参数相同的类型基本数据类型。并且通过引用不计入其他类型。

答案 2 :(得分:3)

您可以使用模板执行此操作:

template<typename T> void foo(T x) { ... }

然后您可以按值或引用来调用此模板:

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

答案 3 :(得分:2)

您可以使用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; }

答案 4 :(得分:1)

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

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

答案 5 :(得分:1)

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

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

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

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

如果你做的事情变得与众不同使用以下定义:

void foo (int &x);

现在用foo(9)调用它将采用第一种方法,因为你不能将9作为非const int引用传递。

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