Правила инициализации баз и членов указаны в [class.base.init] .
В частности, p7 :
A mem-initializer , где mem-initializer-id обозначает виртуальный базовый класс, игнорируется во время выполнения конструктора любого класса, который не является самым производным классом.
и его дополнение в p13 :
Сначала и только для конструктора самого производного класса ([intro.object]), виртуальная базаклассы инициализируются в том порядке, в котором они отображаются при обходе слева направо по глубине направленного ациклического графа базовых классов, где «слева направо» - порядок появления базовых классов в производном классе base-specier-list .
Следовательно, инициализаторы B(true)
и B(false)
игнорируются при инициализации Mf
и Mt
, поскольку они не являются наиболее производным классоми инициализация D
приводит с thИнициализация B
.Инициализатор для него не предусмотрен, поэтому используется B()
.
Сделать это не скомпилированным будет в принципе невозможно?Для начала рассмотрим:
struct Mf : public virtual B { };
struct D : public Mf { };
Это инициализирует B
, но неявно.Вы хотите, чтобы это было ошибкой для Mf
, поскольку его инициализация будет игнорироваться?Я предполагаю нет - в противном случае эта языковая функция была бы совершенно непригоднаА как насчет:
struct Mf : public virtual B { Mf() : B() { } };
struct D : public Mf { };
Это ошибка?Это в основном означает то же самое, хотя.Что, если у Mf
есть члены, которые нужно инициализировать, и я, по привычке, точно так же, как перечисляю базовые классы?
struct Mf : public virtual B { Mf() : B(), i(42) { } int i; };
struct D : public Mf { };
Хорошо, вы говорите, вы допускаете ошибку, только если вы фактически предоставляете аргументы.Вот где возникает другое заблуждение:
Мы знаем из предыдущих вопросов, что при построении наиболее производный класс напрямую вызывает конструктор по умолчанию (0-arg) (виртуальной) базы.
Это не правда (и это не то, что утверждают эти ответы).Самый производный класс инициализирует виртуальные базы - но эта инициализация не должна быть по умолчанию.Мы могли бы написать:
struct D : public Mf, public Mt { D() : B(true) { } };
И действительно, нет никакого интересного различия между B()
и B(true)
.Представьте, что конструктор был просто B(bool = true)
, тогда имеет ли значение, предоставит ли пользователь аргумент true
?Было бы странно, если бы одна была ошибкой, а не другой, верно?
Если вы продолжите спускаться по этой кроличьей норе, я думаю, вы обнаружите, что допускать эту ошибку было бы либо слишком узким, либо чрезмерно ограничительным,