简体   繁体   中英

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. 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) 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!)

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:

#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++. 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 .

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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