Ошибка MSVC9.0 или недоразумение виртуального наследства и друзей?

Рассмотрим следующий код:

class A
{
    friend class B;
    friend class C;
};

class B: virtual private A
{
};

class C: private B
{
};

int main()
{
    C x; //OK default constructor generated by compiler
    C y = x; //compiler error: copy-constructor unavailable in C
    y = x; //compiler error: assignment operator unavailable in C 
}

MSVC9.0 (компилятор C++ Visual Studio 2008) генерирует конструктор по умолчанию, но не может генерировать операторы копирования и назначения для C, хотя C является другом A. Это ожидаемое поведение или это ошибка Microsoft? Я думаю, что последний случай, и если я прав, может кто-нибудь указать на статью / форум /..., где обсуждается эта проблема или где Microsoft отреагировала на эту ошибку. Заранее спасибо.

PS Кстати, если ОБА частное наследство меняется на защищенное, все работает

PPS Мне нужно доказательство того, что приведенный выше код является законным или незаконным. На самом деле предполагалось, что класс с виртуальной частной базой не может быть получен, как я понимаю. Но они, кажется, пропустили часть друга. Итак... вот и моя первая награда:)

4 ответа

Решение

Ваш код прекрасно компилируется с Comeau Online, а также с MinGW g++ 4.4.1.

Я упоминаю, что это просто "аргумент власти".

От стандарта POV доступ является ортогональным к виртуальному наследованию. Единственная проблема с виртуальным наследованием состоит в том, что это самый производный класс, который инициализирует класс фактически производного от далее по цепочке наследования (или "как будто"). В вашем случае наиболее производный класс имеет требуемый доступ для этого, и поэтому код должен компилироваться.

MSVC также имеет некоторые другие проблемы с виртуальным наследованием.

Так да,

  • код действителен, и
  • это ошибка компилятора MSVC.

К вашему сведению, эта ошибка все еще присутствует в MSVC 10.0. Я нашел сообщение об ошибке о подобной ошибке, подтвержденное Microsoft. Тем не менее, с помощью небольшого поиска, я не смог найти эту конкретную ошибку.

То, как я интерпретирую Стандарт, пример кода хорошо сформирован. (И да, friend объявления имеют большое значение от цитируемой @Steve Townsend.)

11.2p1: Если класс объявлен базовым классом для другого класса, использующего private спецификатор доступа, public а также protected члены базового класса доступны как private члены производного класса.

11.2p4: член m доступен, когда назван в классе N, если

  • м как член N является публичным, или
  • m, поскольку член N является частным, и ссылка встречается в члене или друге класса N, или
  • m как член N защищен, и ссылка встречается у члена или друга класса N или у члена или друга класса P, полученного из N, где m в качестве члена P является частным или защищенным, или
  • существует базовый класс B из N, который доступен в исходной точке, и m доступен, когда назван в классе B.

11.4p1: Друг класса - это функция или класс, который не является членом класса, но ему разрешено использовать закрытые и защищенные имена членов класса.

В Статье 11 (Контроль доступа членов) нет операторов, которые подразумевают, что у друга класса когда-либо меньше прав доступа, чем у класса, который подружился с ним. Обратите внимание, что "доступный" определяется только в контексте определенного класса. Хотя мы иногда говорим о том, что член или базовый класс являются "доступными" или "недоступными" в целом, было бы точнее говорить о том, "доступен ли он во всех контекстах" или "доступен во всех классах" (как в случае когда только public используется).

Теперь о частях, которые описывают проверки контроля доступа автоматически определенными методами.

12.1p7: неявно объявленный конструктор по умолчанию для класса неявно определяется, когда он используется для создания объекта своего типа (1.8). Неявно определенный конструктор по умолчанию выполняет набор инициализаций класса, который будет выполняться пользовательским конструктором по умолчанию для этого класса с пустым списком mem-initializer-list (12.6.2) и пустым телом функции. Если этот пользовательский конструктор по умолчанию будет неверно сформирован, программа будет некорректно сформирована.

12.6.2p6: Все подобъекты, представляющие виртуальные базовые классы, инициализируются конструктором самого производного класса (1.8). Если конструктор самого производного класса не указывает mem-initializer для виртуального базового класса V, затем VКонструктор по умолчанию вызывается для инициализации подобъекта виртуального базового класса. Если V не имеет доступного конструктора по умолчанию, инициализация некорректна.

12.4p5: неявно объявленный деструктор неявно определяется, когда он используется для уничтожения объекта своего типа (3.7). Программа плохо сформирована, если класс, для которого неявно определен деструктор, имеет:

  • нестатический член данных типа класса (или его массив) с недоступным деструктором, или
  • базовый класс с недоступным деструктором.

12.8p7: неявно объявленный конструктор копирования неявно определяется, если он используется для инициализации объекта его типа класса из копии объекта его типа класса или типа класса, производного от его типа класса. [Примечание: конструктор копирования неявно определяется, даже если реализация исключает его использование (12.2).] Программа плохо сформирована, если класс, для которого неявно определен конструктор копирования, имеет:

  • нестатический член данных типа класса (или его массив) с недоступным или неоднозначным конструктором копирования, или
  • базовый класс с недоступным или неоднозначным конструктором копирования.

12.8p12: Программа плохо сформирована, если класс, для которого неявно определен оператор присваивания копии:

  • нестатический элемент данных const тип или
  • нестатический элемент данных ссылочного типа, или
  • нестатический член данных типа класса (или его массив) с недоступным оператором назначения копирования, или
  • базовый класс с недоступным оператором присваивания копии.

Все эти требования, упоминающие "недоступный" или "доступный", должны интерпретироваться в контексте некоторого класса, и единственный класс, который имеет смысл, - это тот, для которого функция-член неявно определена.

В исходном примере class A неявно имеет public конструктор по умолчанию, деструктор, конструктор копирования и оператор присваивания копии. По 11.2p4, так как class C друг class Aвсе эти члены доступны, когда названы в классе C, Таким образом, проверки доступа к этим членам class A не вызывают неявные определения class CКонструктор по умолчанию, деструктор, конструктор копирования или оператор присваивания копии являются некорректными.

Занятия с virtual private база не должна быть получена из, в соответствии с этим в C++ SWG. Компилятор делает правильные вещи (до определенного момента). Проблема не в видимости A из C, а в том, что C вообще нельзя разрешать создавать экземпляры, подразумевая, что ошибка находится в первой (по умолчанию) конструкции, а не в других строках.

  1. Может ли класс с частным виртуальным базовым классом быть получен из?

Раздел: 11.2 [class.access.base]
Статус: NAD Отправитель: Джейсон Меррилл Дата: неизвестно

class Foo { public: Foo() {}  ~Foo() {} };
class A : virtual private Foo { public: A() {}  ~A() {} };
class Bar : public A { public: Bar() {}  ~Bar() {} }; 

~Bar() вызывает ~Foo(), который плохо сформирован из-за нарушения прав доступа, верно? (У конструктора Bar та же проблема, так как он должен вызывать конструктор Foo.) Кажется, между компиляторами есть некоторые разногласия. Sun, IBM и g++ отвергают тестовый сценарий, EDG и HP принимают его. Возможно, этот случай следует пояснить запиской в ​​проекте. Короче говоря, похоже, что класс с виртуальной частной базой не может быть получен из.

Обоснование: Это то, что было задумано.

Кстати, поведение компилятора Visual C++ v10 такое же, как отмечено в вопросе. Удаление virtual от наследства A в B устраняет проблему

Я думаю, что это не ошибка. Просто C должен быть другом B, чтобы иметь возможность скопировать его виртуальный базовый класс A.

Другие вопросы по тегам