31

是否声明如下内容

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.
4

6 回答 6

11

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

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

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

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

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

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

于 2011-03-28T21:37:07.360 回答
6

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

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

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

int x = 123;
foo<int>(x);  // by value
foo<int const&>(x);  // by refernce
于 2011-03-29T02:42:41.813 回答
3

调用者如何区分它们?

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

于 2011-03-28T21:34:41.867 回答
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; }
于 2020-11-21T14:52:17.127 回答
1

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

签名中的const关键字定义的不是参数的类型,而是它在函数中的可变性;如果由const函数修改或通过引用传递给任何不使用const.

于 2011-03-28T21:33:54.910 回答
1

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

在第一个 foo 中,制作了 int 的副本。复制一个 int 总是可能的。

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

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

如果您使用以下定义,情况会有所不同:

void foo (int &x);

现在调用它将采用foo(9)第一种选择,因为您不能将 9 作为非常量 int 引用传递。

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

于 2011-03-28T21:37:39.037 回答