1

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?

4
  • 1
    Because f can be called both with a copy of b and also with a reference to b. Both are valid here. Thus in f(b); the compiler is not able to tell which overload is the intended one. Commented Mar 15, 2024 at 22:21
  • C++ has a built-in mechanism of conversion from lvalue (e.g. reference) to rvalue (e.g. number). And so, yes, these are different types, but there's an implicit conversion between them. In particular f(int a) can be called with int& and a copy will be passed to it. Commented Mar 15, 2024 at 22:27
  • Why is this happening? The calls are ambiguous. Are int& and int two different types? Yes. If so, why the compiler do not detect the problem in source.cpp where the functions are defined? It's not a problem for definition. It's a problem for invocation (the callsite). Commented Mar 15, 2024 at 22:37
  • ambiguous call to overloaded function - int and int& Commented Mar 16, 2024 at 2:37

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

Comments

2

The comments to your question has good explanation.

But for some obvious reasons if you want to compile your code, make the second integer declaration const (I mean const int& b = a;).

#include "iostream"

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

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

int main()
{
    int a = 1;
    const int& b = a;
    f(b); //But this will call f(int) only. Refer comments to your question.
}

1 Comment

Side note: You can't always count on the comments being there later. I recommend summarizing the important bits.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.