In N3242 (the standard draft I have on hand), 13.3.1 paragraph 4 says
the type of the implicit object parameter is "lvalue reference to cv X” for [non-static member] functions declared without a
ref-qualifier or with the & ref-qualifier
this means that type of the implicit object argument, which occurs first, is an "lvalue reference to cv X
", where X
is the class, and cv
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 the const
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 a const
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.
Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if ...
S1 and S2 are reference bindings, 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.
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
Standard says in 8.3.5/3 that for the purposes of determining the function type any cv-qualifiers that directly qualify the parameter type are deleted. I.e. it literally says that a function declared as
has function type
void (int *)
.A pedantic person might argue that this is not conclusive enough to claim that the above declaration should match the definition like this one
or that it should make the code with dual declaration (as in your example) ill-formed, since neither of these concepts are described in the standard in terms of function types.
I mean, we all know that these
const
were intended to be ignored for all external purposes, but so far I was unable to find the wording in the standard that would conclusively state exactly that. Maybe I missed something.Actually, in 13.1/3 it has a "Note" that says that function declarations with equivalent parameter declarations (as defined in 8.3.5) declare the same function. But it is just a note, it is non-normative, which suggests that somewhere in the standard there should be some normative text on the same issue.