Search code examples
c++overloadingpass-by-referencepass-by-valueambiguous

Ambiguous call to function when overloading with reference types


I'm overloading a function f with two different parameter type.

In source.h, I declare two functions.

void f(int&);
void f(int);

That it, I have two functions, one accepting a reference type and the other a value (pass-by-reference and pass-by-value, respectively).

In source.cpp I define them

void f(int& a){
    a + 1;
}

void f(int a){
    a + 1;
}

Then, in main function

int main() {
    int a = 1;
    int& b = a;
    f(b);
}

The compiler (clang-tidy) detects a problem: Call to f is ambiguous. Why is this happening? Are int& and int two different types? If so, why the compiler do not detect the problem in source.cpp where the functions are defined?


Solution

  • Why is this happening? Are int& and int two different types?

    It is wrong to think that passing a variable name to a function means that the argument has the exact same type as the variable.

    References in C++ are not exactly distinct types. In particular an expression, such as the id-expression naming the variable used as a function argument, never has a reference type. If you name a reference variable as an id-expression, then the id-expression has the referenced type of the variable's reference type.

    The point of references in C++ is that they behave in expressions exactly as if you had named the object that the reference is bound to directly. So whether you write f(a) or f(b) the result should be exactly the same.

    Therefore the fact that b's type is reference-qualified can't matter to overload resolution. Overload resolution only knows that the argument is of type int and that it is an lvalue expression (i.e. an expression referring to an object instead of an abstract value).

    Both function overloads are then viable and there isn't any clear preference one would have for one over the other, so the rules say that such an overload attempt between a reference and non-reference parameter in which the conversion sequences are equally ranked (identity conversion sequence here in both cases), are ambiguous.

    If so, why the compiler do not detect the problem in source.cpp where the functions are defined?

    The compiler will detect the problem in main.cpp, because the problem will force it to fail compilation. If your compiler didn't complain, then that's a bug in the compiler.

    There is no issue in source.cpp because there is absolutely nothing wrong with just defining the two function overloads. The only problem is that calling them with an int lvalue as argument is ambiguous. If you tried to pass something else, e.g. a long, then only one of the two overloads would be viable and could be chosen. There is nothing in general wrong with having the two overloads.