Ошибка MSVC9.0 или недоразумение виртуального наследования и друзей? - PullRequest
4 голосов
/ 10 октября 2010

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

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 отреагировала на эту ошибку. Заранее спасибо.

P.S. Между прочим, если оба частных наследства изменяются на защищенные, все работает

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

Ответы [ 4 ]

2 голосов
/ 12 октября 2010

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

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

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

  • m в качестве члена 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.

1 голос
/ 10 октября 2010

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

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

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

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

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

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

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

1 голос
/ 12 октября 2010

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

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

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

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

Итак, да,

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

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

0 голосов
/ 10 октября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...