Why is a public const method not called when the non-const one is private?

前端 未结 11 984
天涯浪人
天涯浪人 2020-12-25 09:18

Consider this code:

struct A
{
    void foo() const
    {
        std::cout << \"const\" << std::endl;
    }

    private:

        void foo()
           


        
相关标签:
11条回答
  • 2020-12-25 10:00

    Since the implicit this pointer is non-const, the compiler will first check for the presence of a non-const version of the function before a const version.

    If you explicitly mark the non-const one private then the resolution will fail, and the compiler will not continue searching.

    0 讨论(0)
  • 2020-12-25 10:02

    When you call a.foo();, the compiler goes through overload resolution to find the best function to use. When it builds the overload set it finds

    void foo() const
    

    and

    void foo()
    

    Now, since a is not const, the non-const version is the best match, so the compiler picks void foo(). Then the access restrictions are put in place and you get a compiler error, since void foo() is private.

    Remember, in overload resolution it is not 'find the best usable function'. It is 'find the best function and try to use it'. If it can't because of access restrictions or being deleted, then you get a compiler error.

    In other words why does overload resolution comes before access control?

    Well, let's look at:

    struct Base
    {
        void foo() { std::cout << "Base\n"; }
    };
    
    struct Derived : Base
    {
        void foo() { std::cout << "Derived\n"; }
    };
    
    struct Foo
    {
        void foo(Base * b) { b->foo(); }
    private:
        void foo(Derived * d) { d->foo(); }
    };
    
    int main()
    {
        Derived d;
        Foo f;
        f.foo(&d);
    }
    

    Now let's say that I did not actually mean to make void foo(Derived * d) private. If access control came first then this program would compile and run and Base would be printed. This could be very hard to track down in a large code base. Since access control comes after overload resolution I get a nice compiler error telling me the function I want it to call cannot be called, and I can find the bug a lot easier.

    0 讨论(0)
  • 2020-12-25 10:03

    Ultimately this comes down to the assertion in the standard that accessibility should not be taken into consideration when performing overload resolution. This assertion may be found in [over.match] clause 3:

    ... When overload resolution succeeds, and the best viable function is not accessible (Clause [class.access]) in the context in which it is used, the program is ill-formed.

    and also the Note in clause 1 of the same section:

    [ 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 ]

    As for why, I can think of a couple of possible motivations:

    1. It prevents unexpected changes of behaviour as a result of changing the accessibility of an overload candidate (instead, a compile error will occur).
    2. It removes context-dependence from the overload resolution process (i.e. overload resolution would have the same result whether inside or outside the class).
    0 讨论(0)
  • 2020-12-25 10:03

    In this call:

    a.foo();
    

    There is always an implicit this pointer available in every member function. And the const qualification of this is taken from the calling reference/object. The above call is treated by the compiler as:

    A::foo(a);
    

    But you have two declarations of A::foo which is treated like:

    A::foo(A* );
    A::foo(A const* );
    

    By overload resolution, the first will be selected for non-const this, the second will be selected for a const this. If you remove the first, the second will bind to both const and non-const this.

    After overload resolution to select the best viable function, comes access control. Since you specified access to the chosen overload as private, the compiler will then complain.

    The standard says so:

    [class.access/4]: ...In the case of overloaded function names, access control is applied to the function selected by overload resolution....

    But if you do this:

    A a;
    const A& ac = a;
    ac.foo();
    

    Then, only the const overload will be fit.

    0 讨论(0)
  • 2020-12-25 10:03

    Access specifiers do not affect name-lookup and function-call resolution, ever. The function is selected before the compiler checks whether the call should trigger an access violation.

    This way, if you change an access specifier, you'll be alerted at compile-time if there is a violation in existing code; if privacy were taken into account for function call resolution, your program's behavior could silently change.

    0 讨论(0)
提交回复
热议问题