简体   繁体   English

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

[英]Function Overloading Based on Value vs. Const Reference

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.

The intent seems to be to differenciate between invocations with temporaries (ie 9 ) and 'regular' argument passing.目的似乎是区分具有临时性(即9 )和“常规”参数传递的调用。 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++ 语言标准没有提供专门针对参数的“l/r 值”重载的方法 - 作为参数传递给函数的任何 l 值都可以隐式转换为引用,因此歧义是不可避免。

C++11 introduces a new tool for a similar purpose — using r-value references , you can overload as follows C++11 引入了一个类似目的的新工具——使用r-value 引用,你可以重载如下

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) ...和foo(4) (作为参数传递的临时 r 值)将导致编译器在int i = 2; foo(i)时选择第二个重载int i = 2; foo(i) int i = 2; foo(i) would pick the first. int i = 2; foo(i)会选择第一个。

( note : even with the new toolchain, it is not possible to differentiate between the cases 2 and 3 in your sample!) 注意:即使使用新工具链,也无法区分示例中的案例 2 和案例 3!)

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

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.并且通过引用并不计入不同的类型。

You can use static_cast to explicitly select the overload to be called:您可以使用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);

}

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; }

Not in C++.不是在 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; Erlang 和 Haskell 等函数式语言通过允许您根据参数值指定函数重载而更加接近,但包括 C++ 在内的大多数命令式语言都需要基于方法签名的重载; 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;签名中的const关键字定义的不是参数的类型,而是它在函数内的可变性; 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 .如果被函数修改或通过引用传递给任何不使用const函数,“ const ”参数将产生编译器错误。

The compiler can't.编译器不能。 Both definitions of foo can be used for all 'variants' of int. foo 的两个定义都可以用于 int 的所有“变体”。

In the first foo, a copy of the int is made.在第一个 foo 中,制作了 int 的副本。 Copying an int is always possible.复制 int 总是可能的。

In the second foo, a reference to a const int is passed.在第二个 foo 中,传递了对 const int 的引用。 Since any int can be cast to a const int, a reference to it can be passed as well.由于任何 int 都可以转换为 const int,因此也可以传递对它的引用。

Since both variants are valid in all cases, the compiler can't choose.由于这两种变体在所有情况下都有效,因此编译器无法选择。

Things become different if you eg 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.现在用foo(9)调用它将采用第一种选择,因为您不能将 9 作为非常量 int 引用传递。

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.另一个例子,如果你用一个复制构造函数是私有的类来替换 int ,那么调用者就不能复制这个值,第一个 foo 变体将不会被使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM