Ambiguous Reference/Value Versions of Functions

2019-05-07 15:12发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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



回答4:

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.