基于值与常量引用的函数重载

发布于 2024-10-27 22:03:13 字数 412 浏览 3 评论 0原文

声明类似以下内容是否

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.

Does declaring something like the following

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

ever make sense? How would the caller be able to differentiate between them? I've tried

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不乱于心 2024-11-03 22:03:13

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

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

C++11 引入了一个用于类似目的的新工具 - 使用r-值引用,您可以按如下方式重载

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

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

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

The intent seems to be to differenciate between invocations with temporaries (i.e. 9) and 'regular' argument passing. The first case may allow the function implementation to employ optimizations since it is clear that the arguments will be disposed afterwards (which is absolutely senseless for integer literals, but may make sense for user-defined objects).

However, the current C++ language standard does not offer a way to overload specifically for the 'l/r-valueness' of arguments - any l-value being passed as argument to a function can be implicitly converted to a reference, so the ambiguity is unavoidable.

C++11 introduces a new tool for a similar purpose — using r-value references, you can overload as follows

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

... and foo(4) (a temporary, r-value passed as argument) would cause the compiler to pick the second overload while int i = 2; foo(i) would pick the first.

(note: even with the new toolchain, it is not possible to differentiate between the cases 2 and 3 in your sample!)

栀子花开つ 2024-11-03 22:03:13

您可以使用模板来完成此操作:

template; void foo(T x) { ... }

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

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

You could do this with a template:

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

Then you can call this template by value or by reference:

int x = 123;
foo<int>(x);  // by value
foo<int const&>(x);  // by refernce
刘备忘录 2024-11-03 22:03:13

调用者如何区分它们?

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

How would the caller be able to differentiate between them?

It cannot be differentiated in this case. Both the overloaded functions have the same type of primitive data type as the argument. And taking by reference doesn't count for a different type.

猥︴琐丶欲为 2024-11-03 22:03:13

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

You can use static_cast to explicitly select the overload to be called:

#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);

}

However, you should ask yourself why you provided those two overloads in the first place. Either you are fine with making a copy or you are not. Both at the same time? Why? Also making it necessary for the caller to explicitly select the overload defeats the purpse of function overloading. If you really want that consider to supply two functions instead:

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; }
海风掠过北极光 2024-11-03 22:03:13

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

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

Not in C++. Functional languages such as Erlang and Haskell get closer by allowing you to specify function overloads based on parameter value, but most imperative languages including C++ require overloading based on method signature; that is, the number and type of each parameter and the type of the return value.

The const keyword in the signature defines not the type of the parameter, but its mutability within the function; a "const" parameter will generate a compiler error if modified by the function or passed by reference to any function that doesn't also use const.

花开雨落又逢春i 2024-11-03 22:03:13

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

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

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

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

如果您使用以下定义,事情就会变得不同:

void foo (int &x);

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

另一个例子,如果将 int 替换为复制构造函数是私有的类,则调用者无法复制该值,并且将不会使用第一个 foo 变体。

The compiler can't.
Both definitions of foo can be used for all 'variants' of int.

In the first foo, a copy of the int is made. Copying an int is always possible.

In the second foo, a reference to a const int is passed. Since any int can be cast to a const int, a reference to it can be passed as well.

Since both variants are valid in all cases, the compiler can't choose.

Things become different if you e.g. use the following definition:

void foo (int &x);

Now calling it with foo(9) will take the first alternative, since you can't pass 9 as a non-const int reference.

Another example, if you replace int by a class where the copy constructor is private, then the caller can't make a copy of the value, and the first foo-variant will not be used.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文