Недоступный базовый класс, несмотря на дружбу - PullRequest
11 голосов
/ 19 июня 2019

Есть множество вопросов относительно этой ошибки, и все ответы, кажется, подразумевают, что уныние невозможно. Только этот ответ упоминает дружбу как возможное решение, по крайней мере, насколько я понимаю. Однако следующий код (несоответствующие вещи удалены для ясности) не компилируется:

class C;

class A {
    friend class C;  // this does not help
};

class B : protected A {
    friend class C;  // this does not help either
};

class C {
    public:
    void foo(A* a) {};
};

B b;
C c;

void bar()
{
    c.foo(&b);  // this produces error: class A is an inaccessible base of B
}

Почему дружба не работает по ссылке? В конце концов, «C» вполне может вызывать защищенные методы «A» через указатель на «B».

Полная ошибка

prog.cc: In function 'void bar()':
prog.cc:20:13: error: 'A' is an inaccessible base of 'B'
   20 |     c.foo(&b);

Ответы [ 3 ]

14 голосов
/ 19 июня 2019

Ваш код эквивалентен этому:

B b;
C c;
A * a = &b; // <- This cast produces the error
c.foo(a);

Вы не можете разыграть &b как A*, поскольку базовый класс защищен, независимо от дружбы C.

13 голосов
/ 19 июня 2019

Проблема в том, что преобразование из B* в A* (то, которое требует дружбы) происходит не в функции-члене C, а в контексте кода, содержащего b и c (т.е. несвязанная функция bar()).

Было бы хорошо, если бы вы создали функцию-член в C, принимая B*, а затем вызвали foo() из нее. Это произвело бы преобразование в контексте C, который имеет необходимые права доступа (благодаря дружбе).

1 голос
/ 19 июня 2019

В глобальной области видимости B не отображается как A из-за защищенного наследования.

Только сам класс B, классы, унаследованные от B, и класс C (потому что отношение дружбы) "знают", что B наследуетО. Но остальной мир (включая глобальную область видимости) этого не делает.

Таким образом, чтобы достичь того, что вы хотите, вы можете назвать

c.foo(&b)

в области действия C, например, с помощью некоторой оболочкифункция, что-то вроде (хотя и плохое дизайнерское решение):

#include <iostream>
#include <cstdlib>

class C;


class A {
    friend class C;  // this does not help
};

class B : protected A {
    friend class C;  // this does not help either
};

class C {
public:
    void foo() {
        B b;
        foo(&b);  // this call is OK within C-scope
    }
private:
    void foo(A* /*a*/) {
        std::cout << "C::foo(A* a)\n";
    };
};


int main()
{
    std::cout << "Hello, Wandbox!" << std::endl;
    B b;
    C c;
    //c.foo(&b);  // this produces error: class A is an inaccessible base of B
    c.foo();      // this is calling c.foo(A*) internally
}

или live :

...