I have the following test code:
#include <string>
#include <iostream>
class CString
{
public:
CString(char const*) {}
};
class TestBed
{
public:
void Comparison(CString const&) { std::cout << "CString Overload" << std::endl; }
void Comparison(std::string const&) { std::cout << "std::string overload" << std::endl; }
};
int main()
{
TestBed tb;
tb.Comparison("Hello World");
}
This code fails to compile because the call to Comparison()
is ambiguous. I expect this behavior.
However, when I make either of the Comparison()
overloads const
, as in: void Comparison(std::string const&) const
or void Comparison(CString const&) const
(but not both), the code compiles and chooses the non-const overload.
Overload resolution rules are pretty complex and I haven't seen anything that describes how const
affects this situation. My understanding is:
In both cases, 1 & 2 are ambiguous. Can someone explain this? Thanks.
For class methods, the this
part is considered as if it were an extra argument. So if you made the CString
one const, that makes the overload set:
Comparison(const TestBed&, CString const&) // (1)
Comparison(TestBed&, std::string const&) // (2)
For (1)
, we need to do two conversions: a const
conversion, and a conversion to CString
. But for (2)
, we only need to do a single conversion: to std::string
. Thus, (2)
is preferred.
We can verify this by adding a third function that does one single conversion for this
:
Comparison(const TestBed&, const char*) // (3)
Here, we again only have a single conversion (in the "first" argument), and thus the overload set is ambiguous.
In [over.match.funcs]:
a member function is considered to have an extra parameter, called the implicit object parameter, which represents the object for which the member function has been called. For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
For non-static member functions, the type of the implicit object parameter is
— “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
— “rvalue reference to cv X” for functions declared with the && ref-qualifier
where X is the class of which the function is a member and cv is the cv-qualification on the member function declaration. [ Example: for a const member function of class X, the extra parameter is assumed to have type “reference to const X”. —end example ]
During overload resolution, the implied object argument is indistinguishable from other arguments.
That establishes why we consider const TestBed&
vs TestBed&
. And then it's just a matter of comparison the conversion sequences between the overloads (1)
and (2)
. For the 2nd argument, both conversion sequences are equal, but for the 1st argument, (2)
has a better conversion sequence (namely Exact) - which is why it wins without ambiguity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With