Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does const modifier for member functions affect overload resolution?

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:

  1. Function with exact matching argument is chosen first
  2. 1-level implicit conversion is tried next

In both cases, 1 & 2 are ambiguous. Can someone explain this? Thanks.

like image 476
void.pointer Avatar asked Jan 15 '15 21:01

void.pointer


1 Answers

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.

like image 104
Barry Avatar answered Nov 15 '22 19:11

Barry