Ответ 1
Стандарт С++ 11 говорит в 12.6.2/10:
В конструкторе без делегирования инициализация продолжается в в следующем порядке:
- Во-первых, и только для конструктора самого производного класса (1.8) виртуальные базовые классы инициализируются в порядке они появляются на первом переходе слева направо направо ациклический граф базовых классов, где "слева направо" - это порядок появление базовых классов в производном классе базовый спецификатор-лист.
- [прямые базовые классы и т.д....]
Это говорит об этом, в основном - самый производный класс отвечает за инициализацию каким бы способом он ни определялся (в OP: это не так, что приводит к инициализации по умолчанию). Следующий пример в стандарте содержит аналогичный сценарий, как в OP здесь, только с аргументом int для ctor; вызывается только по умолчанию ctor виртуальной базы, поскольку явный "mem-initializer" для виртуальной базы не предоставляется в самом производном классе.
Интерес, хотя и не прямо применяемый здесь, также составляет 12.6.2/7:
Память-инициализатор [the
A()
в возможномB(): A() {}
. -pas], где идентификатор mem-initializer обозначает виртуальную базу класс игнорируется во время выполнения конструктора любого класса, который не является самым производным классом.
(Я нахожу это довольно жестким. Язык в основном говорит: "Мне все равно, что вы закодировали, я проигнорирую его". Там не так много мест, где он может это сделать, нарушая как-если.) Этот конструктор не самого производного класса будет B()
. Предложение здесь не применяется непосредственно, потому что в B
нет явного конструктора, поэтому нет mem-инициализатора. Но хотя я не мог найти формулировки для этого в стандарте, нужно предположить (и это согласуется), что то же правило применяется для сгенерированного конструктора копирования.
Для полноты, Stroustrup говорит в "языке программирования С++" (4.ed, 21.2.5.1) о самом производном классе D с виртуальной базой V по дороге где-то:
Тот факт, что V явно не упоминается как основа D, не имеет значения. Знание виртуальной базы и обязательство инициализировать ее "пузырится" до самого производного класса. Виртуальная база всегда считается прямой базой своего самого производного класса.
Именно это сказал Сэм Варшавчик в более раннем посте.
Далее, Stroustrup обсуждает, что получение класса DD из D делает необходимым переместить V-инициализацию в DD, что "может быть неприятным. Это должно побуждать нас не злоупотреблять виртуальными базовыми классами".
Я нахожу довольно неясным и опасным, что базовый класс остается неинициализированным (ну, точнее: инициализированным по умолчанию), если только сам производный класс явно что-то делает.
Автор самого производного класса должен глубоко погрузиться в иерархия наследования s/он может не интересоваться или документировать и не может полагаться на, например, библиотека, которую он/она использует, чтобы делать правильные вещи (библиотека не может).
Я также не уверен, что согласен с обоснованием, данным в других сообщениях ( "какой из промежуточных классов должен выполнять инициализацию?" ). Стандарт имеет четкое представление о порядке инициализации ( "пересечение глубины влево-вправо" ). Не мог ли он указать, что первый класс, который фактически наследует базу, выполняет инициализацию?
Интересный факт, что копия ctor по умолчанию инициализирует виртуальную базу, предписывается в 12.8/15:
Каждый базовый или нестатический элемент данных копируется/перемещается способом соответствующий его типу:
[...]
- в противном случае основание или член с прямой инициализацией с соответствующей базой или членом x.Виртуальный субобъекты базового класса должны быть инициализированы только один раз неявно заданный конструктор копирования/перемещения (см. 12.6.2).
В любом случае, поскольку C
является самым производным классом, он отвечает за C
(а не B
) ответственность за копирование-построение виртуальной базы A
.