2
#include<iostream> using namespace std; void foo(const long& a) { cout << "long version"; } void foo( float& a) { cout << "float version" << endl; } int main() { int a = 3; foo(a); } 

The example above will output "long version", however, according to C++ primer:

When we call an overloaded function that differs on whether a reference or pointer parameter refers or points to const, the compiler uses the constness of the argument to decide which function to call:

The program should output the version that does not contain the constness since they have the same arithmetic conversion precedence, but the program always chooses the one with const for some reason.

Could anyone explain to me why does the compiler choose the const version? Really appreciate your help :)

2
  • If this is your actual code, I don't see how it can output "int version". That doesn't exist. Please show the exact code you're experiencing this issue with. Commented Dec 19, 2019 at 4:27
  • I typed the wrong text, fixed, and thanks for reminding Commented Dec 19, 2019 at 4:33

2 Answers 2

3

You are calling the function with an argument of type int, but the function is taking a reference to a long or float, which are different types, as parameter. You cannot bind a reference of one type to another type (up to const/volatile qualifiers and if they are not related through a class hierarchy).

What the compiler therefore tries to do is to create a temporary object of the parameter type initialized from the int argument by conversion (arithmetic conversion) and binds the reference to that.

Temporaries are rvalues, meaning that they cannot be bound to non-const lvalue references. Therefore, after creating a float object from the int passed to it, foos parameter would not be able to bind to it. Consequently the float overload is not viable.

The only overload left is the long one. This one is viable, because after creating the temporary long object, it can bind to a const lvalue reference.

And so the long version is called.


The quote you showed applies (for the most part) only if the argument and the parameters' types match (up to const) and the argument itself is an lvalue. I assume you missed this relevant context surrounding the quoted section.

You cannot call a function taking a non-const lvalue reference with a different type as argument (except if the types are related through a class hierarchy), nor can you call it with a rvalue as argument.

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

Comments

1

What you seem to be hoping for, is that the compiler will automagically do:

 int a = 3; foo(float(a)); 

The problem here, is that float(a) is effectively a temporary object, and that object happens to be const. Since foo takes a float&, the compiler cannot match the constant temp, to a non-const (aka mutable) argument. However, the compiler can do this:

 int a = 3; foo(long(a)); 

Since the argument for foo is const when using long. Given the compiler cannot use the float version, it will always pick the const long version instead.

3 Comments

"and that object happens to be const": That is wrong. It is not const, but it is a rvalue and so cannot be bound to a non-const lvalue reference.
@robthebloke so you mean we are actually passing a const copy of variable "a" instead of the original a, therefore, the compiler prefers the const one because a plain reference can not yield to a const reference?
so is this copy a lvalue or rvalue?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.