5

Consider the following function prototypes:

void Remove(SomeContainer& Vec, const std::size_t Index);

SomeContainer Remove(SomeContainer Vec, const std::size_t Index);

The second is implemented in terms of the first. That is to say, they are functionally identical in every way except that one is pass-by-reference and the other is pass-by-value.

However, GCC says these are ambiguous in cases like this, even though the first form is the only one that does not return a value:

Remove(SomeContainer, 123);

Is there any workaround to this, or do I have to come up with different names for each form?

Lstor
  • 2,265
  • 17
  • 25
Maxpm
  • 24,113
  • 33
  • 111
  • 170

4 Answers4

5

Return type is not an basis of function overloading.
Overloading of functions can only be with one of the following criteria:

  1. No of arguments
  2. Type of arguments &
  3. Sequence of arguments

The return type can be ignored by the caller and hence it is not a valid criteria for function overloading.

Having said the above, pass by value and passing a Reference will create a ambiguity to the compiler. For eg:

void doSomething(int i)
{
}

void doSomething(int &i)
{
}

int main()
{
    int val = 10;
    doSomething(val);   //Ambiguous
}

Here the compiler cannot determine as to pass val to which version of doSomething(). It can make a valid function call to any of the versions, so it cries out for help at compile time(since this is static linking) and flags the calls as ambiguous.

In case such as yours. It is a choice/preference as to rename the functions or pass pointer argument which will make the two functions overloaded(same name but different argument types). However, it is important to take in to account the requirement & the action the function is going to perform while choosing the preference. Personally, I wouldn't choose a pointer just for sake of overloading. If I do need to reseat or make my argument point to different variables then it would make sense to choose pointer argument.

Simple way is to just have two distinct function names. There is no overhead and it is just as efficient as any other function call.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Pass-by-reference/value is ignored as well, then, I'm assuming? – Maxpm May 02 '11 at 12:53
  • 2
    Pass-by-reference and pass-by-value counts as different types, and is perfectly fine. In C++, potential ambiguity is not an error. The problem is when trying to invoke it, as the compiler has no idea whether a `5` is supposed to be an `int` or an `int&`. – Lstor May 02 '11 at 12:56
  • @Als What's commonly done in situations like this, then? Are the functions just renamed, or is the form that accepts a parameter changed to accept a pointer instead? – Maxpm May 02 '11 at 12:58
  • @Oli: Right, I was thinking if you have `int x = 5; foo(x);`. Definitely badly worded on my part. – Lstor May 02 '11 at 13:09
  • @Als: No, it's not true. `int` and `int&` are considered different types. – Lstor May 02 '11 at 13:10
3

As mentioned, the return type is not considered for overloading. However, the compiler does consider plain-value and references different types, but it will normally not know which version to call. In other words, having two overloaded functions that are different only in whether a parameter is pass-by-value or pass-by-reference is fine up until you try to call it: Potential ambiguity is not an error in C++.

Example:

void f(int) {
    cout << "value\n";
}

void f(int&) {
    cout << "reference\n";
}

int main() {
    int  val = 42;

    f(val); // Error! Ambiguous.
    f(static_cast<int>(val)); // OK: The type is int. Will print "value"
}

I do not know how to signal that you want f(int&), however, so there is not much practical use in this -- I'm just trying to clarify how C++ overloading works.

Lstor
  • 2,265
  • 17
  • 25
2

You could help the compiler a bit, and the users of your functions, by choosing more distinguishing names:

Container Removed( const Container& c, size_t index );
void Remove( Container& c, size_t index );

Adding const to the immutable version will also inhibit users from accidentally calling the imperative variant (the compiler won't allow it, at least not for const containers).

xtofl
  • 40,723
  • 12
  • 105
  • 192
1

Pass by reference/value is not used to determine function overloading because there is no way of the compiler knowing which is required - both are equally good matches for a value passed as a parameter. And as others point out, return type is never considered.

  • 2
    That is not correct, as I pointed out above. You may have both `void f(int)` and `void f(int&)` - the problem is when trying to call it. – Lstor May 02 '11 at 12:59
  • 1
    What I mean is the "not used to determine function overloading" part. It _is_: defining void f(int) and void f(int&) will compile just fine, because the compiler considers them different overloads. – Lstor May 02 '11 at 13:12
  • I would also expect that if you call `f(5);` this should call the `int` version since you can't pass a reference to `5`. But `int x = 5; f(x);` will give an error about ambiguity. – PieterNuyts Dec 18 '19 at 14:36