С++: Можно ли наследовать от класса и его защищенного типа?

Соответствует ли следующий код C++ стандартным?

struct B
{
protected:
    struct Type {};
};

struct D : B, B::Type
{};

int main()
{
    D d;
    return 0;
}

Я пробовал его в Compiler Explorer. MSVC (VS 2017 RTW) принимает его. gcc (7.3) и clang (6.0.0) отклоняют его.

Ответы

Ответ 1

Код стандартно совместим и был с С++ 11, но не был на С++ 03.

С++ 11 - С++ 17 говорят об этом во введении к разделу [class.access], Access Access Control:

Все элементы управления доступом в разделе [class.access] влияют на возможность доступа к имени члена класса из объявления конкретного объекта, включая части объявления, предшествующие имени объявляемого объекта, и, если объект является классом, определения членов класса, выходящих за пределы спецификации класса.

В тех же стандартных версиях приведен пример, который очень похож на ваш вопрос, но даже немного сложнее:

[Пример:

  class A {
...
  protected:
      struct B { };
  };
...

  struct D: A::B, A { };

... Использование A::B в качестве базового-спецификатора хорошо сформировано, потому что D является производным от A, поэтому проверка базовых спецификаторов должна быть отложена до тех пор, пока не будет замечен весь список-спецификатор базы. -end пример]

Но я вижу те же результаты: g++ и clan g++ оба отклоняют эти программы, независимо от того, что -std= аргумент, который я даю. Это пара ошибок компилятора.

С++ 03 имеет это вместо первого абзаца I, приведенного выше:

Все элементы управления доступом в разделе [class.access] влияют на возможность доступа к имени члена класса из определенной области. Контроль доступа для имен, используемых в определении члена класса, который появляется за пределами определения класса участника, выполняется так, как если бы определение всего члена появилось в области класса-члена...

Базовый-спецификатор определения класса не входит в эту область класса, поэтому С++ 03 не позволяет использовать защищенное или приватное имя в качестве имени базового класса для производного класса, который в противном случае имеет доступ к этому имени.

Ответ 2

Если D получен из B, он имеет доступ к своим защищенным членам, поэтому это должно быть правильно. Вопрос будет в том случае, если у него уже есть доступ в этой точке компиляции или если он получает доступ только после завершения типа.

Обратите внимание, что он должен потерпеть неудачу, если вы включите два (положив B за запятую).