Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trouble with const/non-const overload resolution

I have a class that looks something like this:

class ClassA
{
  public:
    float Get(int num) const;
  protected:
    float& Get(int num);
}

Outside of the class, I call the Get() function.

float foo = classAInstance.Get(i);

I expect this to call the public version, but instead Visual Studio errors out:

error C2248: 'ClassA::Get' : cannot access protected member declared in class 'ClassA'

When commenting out the protected overload and removing all references to it, the code compiles.

Why does the compiler try to use the inaccessible member when an accessible one is available? Is there an accepted way to force the compiler to choose the correct overload? Is there a reference to the resolution rules for member functions somewhere?

like image 296
thekidder Avatar asked Jun 04 '11 21:06

thekidder


2 Answers

It's true, overload resolution takes place before accessibility checks. Section 13.3 of the standard ([over.match]) says:

Overload resolution is a mechanism for selecting the best function to call given a list of expressions that are to be the arguments of the call and a set of candidate functions that can be called based on the context of the call. The selection criteria for the best function are the number of arguments, how well the arguments match the parameter-type-list of the candidate function, how well (for non-static member functions) the object matches the implicit object parameter, and certain other properties of the candidate function. [ Note: The function selected by overload resolution is not guaranteed to be appropriate for the context. Other restrictions, such as the accessibility of the function, can make its use in the calling context ill-formed. — end note ]

The usual fix is to give the public and protected functions different names.


Note, this is useful sometimes, example:

class Blah
{
    const std::string& name_ref;

    Blah(const char*) = delete;

public:
    Blah(const std::string& name) : name_ref(name) {}

    void do_something_with_name_ref() const;
};

std::string s = "Blam";
Blah b(s); // ok

Note that name_ref will only be read from, so it's appropriate to make it const. However, const references can bind to temporaries, and binding name_ref to a temporary would be a dangling reference, resulting in undefined behavior in do_something_with_name_ref().

Blah c("Kablooey!"); // would be undefined behavior
                     // the constructor overload makes this a compile error

The private constructor overload prevents a temporary std::string from being implicitly constructed and bound.

like image 105
Ben Voigt Avatar answered Nov 15 '22 13:11

Ben Voigt


Overload resolution is done first, and access check later.

If you have both a const and a non-const overload, this is resolved by the constness of the object the function is called for.

like image 29
Bo Persson Avatar answered Nov 15 '22 14:11

Bo Persson