использование с именем базового класса для изменения допуска?

Мой друг показал мне следующий код

struct A {
  virtual void f() = 0;
  virtual void g() = 0;
};

struct AInternal : A {
  virtual void f() { /* ... */ }
  virtual void g() { /* ... */ }
};

Он использует AInternal как внутренний класс, который реализует большую часть (если не все A). Затем он унаследовал от AInternal, но поскольку он хотел, чтобы AInternal оставалось недоступным (поскольку это деталь реализации), он наследует защищенный (реализованный в терминах). Он также using изменил имя базового класса, чтобы сделать A доступным (по умолчанию он был защищен, так как AInternal также был унаследован защищенным)

struct C : protected AInternal {
  using AInternal::A;
};

На самом деле, это сработало нормально (но, как мы позже выяснили, функции-члены остались private - был создан только базовый класс public), но это работало только на GCC. Не удается сделать базу A доступной. Любая идея? Мы могли бы даже взломать код, работающий на Clang.

struct C : public AInternal {
protected:
  using AInternal::A;
};

C *c = 0;
A *a = c; // error on GCC!

Может кто-нибудь помочь, пожалуйста?


person Johannes Schaub - litb    schedule 14.07.2013    source источник
comment
Если я правильно понял, то A определяет интерфейс, который должен быть предоставлен C. Чего я на самом деле не понимаю, так это идеи, лежащей в основе всей установки. Это делает общедоступные методы в AInternal недоступными, если они не присутствуют в A, но можно просто сделать такие методы закрытыми в AInternal и наследовать общедоступные в C.   -  person Pixelchemist    schedule 14.07.2013
comment
@Pixelchemist идея заключалась в том, чтобы using AInternal::A снова сделать функции-члены общедоступными. Это не сработало, но оно сделало сделало доступным базовый класс A.   -  person Johannes Schaub - litb    schedule 14.07.2013
comment
Да, но я не понимаю причину такой раскладки. Почему бы не сделать методы интерфейса общедоступными в AInternal, использовать публичное наследование и все будет в порядке? Детали реализации, такие как вспомогательные функции или другие члены, могут оставаться закрытыми в AInternal.   -  person Pixelchemist    schedule 15.07.2013
comment
@ainternal он хотел, чтобы защищался только средний класс. Думаю, лучшим способом было бы объявить using для класса AInternal. Но это не удается, потому что это будет объявление наследующего конструктора.   -  person Johannes Schaub - litb    schedule 15.07.2013


Ответы (1)


Вы влияете только на видимость введенного имени класса. Защита доступа базового подобъекта или его членов не должна затрагиваться. Если Clang или GCC позволяют ему влиять на валидность приведения или доступ к базе, это их ошибка.

[class.member.lookup] 10.2 / 3 говорит

В наборе объявлений объявления-использования заменяются обозначенными ими членами, а объявления типов (включая введенные имена-классов) заменяются типами, которые они обозначают.

Подобъект базового класса не имеет имени при поиске членов; имя-введенного-класса делает.

person Potatoswatter    schedule 15.07.2013
comment
Ну, говори о дьяволе. Я просто нашел случай в моем собственном коде, где я неправильно предположил, что имя внедренного типа имеет ту же квалификацию доступа, что и базовый класс, который его представил. Оказывается, если вы действительно от него зависите, лучше явно определить собственное соленое имя или использовать идиому черт. - person Potatoswatter; 15.07.2013
comment
Спасибо! Я понятия не имел обо всем этом и начал плохо спать из-за видимости базового класса. +1 - person Johannes Schaub - litb; 15.07.2013