Частные методы внезапно недоступны при цепочке

У меня есть простая иерархия классов с базовым классом и производным классом. База имеет два защищенных члена, вызываемых производным классом. Исходя из недавних опытов С#, я подумал, что было бы неплохо сделать интерфейс немного более свободным и позволить цепочку вызовов методов, поэтому вместо вызова this->A(), затем this->B() вы можете позвонить this->A()->B(). Однако следующий код не будет компилироваться:

#include <iostream>

class Base
{
  protected:
    Base* A()
    {
      std::cout << "A called." << std::endl;    
      return this;
    }

    Base* B()
    {
      std::cout << "B called." << std::endl;    
      return this;
    }
};

class Derived : public Base
{
  public:
    void Test()
    {
        // Base::A and Base::B are private here.
        this->A()   // This works fine
            ->B();  // Suddenly I cannot access my own private method?
    }
};

int main()
{
    Derived d;
    d.Test();

    return 0;
}

Это приводит к следующей ошибке компилятора:

main.cpp: In member function 'void Derived::Test()':
main.cpp:12:15: error: 'Base* Base::B()' is protected
         Base* B()
               ^
main.cpp:26:21: error: within this context
                 ->B();  // Suddenly I cannot access my own private method?
                     ^

Я также попытался сделать методы базового класса виртуальными, но это не помогло.

Мой С++ достаточно ржавый, и я не могу понять, что здесь происходит, поэтому помощь будет высоко оценена. Также мне было интересно, если это плохая идея, потому что C++ != C# и С++ - люди не привыкли к таким свободным интерфейсам.

Ответ 1

Защищенный член класса доступен из производного класса только через этот производный класс, то есть через объект или ссылку или указатель на этот производный класс.

Возвращаемым типом A() является Base*, который не является производным классом, поэтому вы не можете получить доступ к его защищенным членам. Компилятор не отслеживает, что он действительно относится к одному и тому же объекту.

Ответ 2

Да, вы не можете вызвать защищенные методы класса Base из Base *. Вы можете думать, что защищенные методы являются частными с той разницей, что они становятся частными из производного класса.

Ответ 3

Это правильное поведение, вы не можете вызвать защищенную функцию для другого класса, который вы можете выполнять только через производный класс, потому что, когда вы вызываете this->A(), он возвращает Base *, который является другим классом. Причина в том, что, если вы сделали что-то вроде

class Derived : public Base
{
  public:
    void Test()
    {
       baseInstance->B(); // this shouldn't be possible. If the case was you can call it through a pointer or an object this would be possible.
    }

    Base* baseInstance; 
};

Также полезно отметить, что производный и базовый this могут не иметь одного и того же адреса, он может иметь другой адрес. Когда вы на самом деле бросаете Base* в Derived*, компилятор будет обрабатывать разницу в адресе, что делает возможным, почему он работает, если это было сделано так: static_cast<Derived*>(this->A())->B();

Ответ 4

Чтобы добавить к Себастьяну, это можно решить, но не красиво, по:

static_cast<Derived*>(this->A())->B();

Ответ 5

Вы можете обратиться к стандарту, чтобы получить ответ на вашу проблему.

11.2 Доступность базовых классов и членов базового класса [class.access.base]

Базовый класс B из N доступен в R, если

— an invented public member of B would be a public member of N, or
— R occurs in a member or friend of class N, and an invented public member of B would be     
    a private or
    protected member of N, or
— R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or protected member of P, or
— there exists a class S such that B is a base class of S accessible at R and S is a base class of N accessible at R

Если вы ссылаетесь на элемент с помощью указателя Base, ни одно из указанных выше статусов.