KS85
KS85

Reputation: 108

Function Matching for parameters of type const T& and T

I have a question regarding the c++ function matching for parameters of types T and const T&. Let's say I have the following two functions:

void f(int i) {}
void f(const int &ri) {}

If I call f with an argument of type const int then this call is of course ambiguous. But why is a call of f with an argument of type int also ambiguous? Wouldn't be the first version of f be an exact match and the second one a worse match, because the int argument must be converted to a const int?

const int ci = 0;
int i = 0;
f(ci); // of course ambiguous
f(i); // why also ambiguous?

I know that such kind of overloading doesn't make much sense, because calls of f are almost always ambiguous unless the parameter type T doesn't have an accessible copy constructor. But I'm just studying the rules of function matching.

Regards, Kevin

EDIT: To make my question more clear. If I have the two functions:

void f(int *pi) {}
void f(const int *pi) {}

Then the following call is not ambiguous:

int i = 0;
f(&i); // not ambiguous, first version f(int*) chosen

Although both versions of f could be called with &i the first version is chosen, because the second version of f would include a conversion to const. That is, the first version is a "better match". But in the two functions:

void f(int i) {} and
void f(const int &ri) {}

This additional conversion to const seems to be ignored for some reason. Again both versions of f could be called with an int. But again, the second version of f would require a conversion to const which would make it a worse match than the first version f(int).

int i = 1;
// f(int) requires no conversion
// f(const int &) does require a const conversion
// so why are both versions treated as "equally good" matches?
// isnt this analogous to the f(int*) and f(const int*) example?
f(i); // why ambiguous this time?

Upvotes: 4

Views: 1112

Answers (3)

Ben Voigt
Ben Voigt

Reputation: 283733

One call involves an "lvalue-to-rvalue conversion", the other requires an identity conversion (for references) or a "qualification adjustment" (for pointers), and according to the Standard these are treated equally when it comes to overload resolution.

enter image description here

So, neither is better on the basis of differing conversions.

There is, however, a special rule in the Standard, section 13.3.3.2, that applies only if both candidates being compared take the parameter by reference.

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if ... S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.

There's an identical rule for pointers.

Therefore the compiler will prefer

f(int*);
f(int&);

over

f(const int*);
f(const int&);

respectively, but there's no preference for f(int) vs f(const int) vs f(const int&), because lvalue-to-rvalue transformation and qualification adjustment are both considered "Exact Match".


Also relevant, from section 13.3.3.1.4:

When a parameter of reference type binds directly to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion.

Upvotes: 4

ssj_100
ssj_100

Reputation: 168

The reason the second call f(i) is ambiguous is because to the compiler, both functions would be acceptable. const-ness can't be used to overload functions because different const versions of functions can be used in a single cause. So in your example:

int i = 0;
fi(i);

How would the compiler know which function you intended in invoking? The const qualifier is only relevant to the function definition.

See const function overloading for a more detailed explanation.

Upvotes: 0

Uchia Itachi
Uchia Itachi

Reputation: 5325

The second call f(i) is also ambiguous because void f(const int &ri) indicates that ri is a reference to i and is a constant. Meaning it says that it will not modify the original i which is passed to that function.

The choice whether to modify the passed argument or not is in the hands of the implementer of the function not the client programmer who mearly uses that function.

Upvotes: 1

Related Questions