In C++, the function signatures
int Test::foo (const int a) const
and
int Test::foo (int a) const
are considered to be complete identical.
The reason that the const
on the parameter is disregarded is because it can not affect the caller in any way. As the parameter is passed by value, a copy is made of the value provided by the caller. To the caller, it does not matter in any way if the called function can change that copy or not.
For this reason, C++ ignores a top-level const
-qualification on function parameters (top-level const
can not occur if passing a reference), and goes even as far that int foo(int);
is considered a correct prototype for the function
int foo(const int)
{
/* ... */
}
In short, it is impossible in C++ to overload a function on the constness of (value) function parameters.
To get the output you want, you could consider using a non-const reference parameter for your non-const overload.
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.
Best Answer
In N3242 (the standard draft I have on hand), 13.3.1 paragraph 4 says
this means that type of the implicit object argument, which occurs first, is an "lvalue reference to
cv X
", whereX
is the class, andcv
is the cv-qualification of the member variable (i.e. const or non-const). Then, overload resolution continues as normal.To review the overload resolution process, first, both are listed as "candidate" functions as they are in the correct scope and have the correct name.
In the
const
case, only theconst
member function gets to the next step (called "viability"), so it's automatically the best choice. The non-const member function is not viable because you can't convert aconst
reference into a non-const reference.In the non-const case, both the const and non-const versions are viable, but the non-const one is "better" because of the fifth rule of 13.3.3.2 paragraph 3, quoted below.