Я наткнулся на вопрос об объявленном с использованием наследующем конструкторе вчера. И после тщательного прочтения ответа, а также связанного стандартного черновика N3337 , я обнаружил, что могут быть некоторые несоответствия (или, по крайней мере, мое недопонимание), когда прямой базовый класс также использует using
для наследования конструкторов от виртуальная база.
Вот пример:
struct A
{
A(int, int){}
};
struct B : virtual A
{
using A::A;
};
struct C : virtual A
{
using A::A;
};
struct BC : B, C
{
using B::B;
using C::C;
};
// Now if we define an inline constructor that does the same
// as the constructor B inherited...
struct BB : virtual A
{
BB(int a, int b):A(a,b){}
};
struct BBC : BB, C
{
using BB::BB;
using C::C;
};
int main()
{
BC(1, 1); // this compiles
BBC(1, 1); // this doesn't because it needs to defaultly
// initialize the virtual base A who doesn't
// have a default constructor
}
Я понимаю, почему BBC
не может скомпилироваться по точной причине, указанной в ответе выше, который я здесь повторю [class.inhctor] / 8
Неявно определенный наследующий конструктор выполняет набор инициализаций класса, который будет выполнен встроенным конструктором, написанным пользователем для этого класса, с помощью mem-initializer-list, чей единственный mem-initializer имеет идентификатор mem-initializer-id, который называет базовый класс, обозначенный в спецификаторе nested-name декларации using и в списке выражений, как указано ниже, и где составной оператор в его функции тело пусто ([class.base.init]).
и [class.base.init] / 10 * 102 2 *
В не делегирующем конструкторе инициализация происходит в следующем порядке: во-первых, и только для конструктора самого производного класса ([intro.object]), виртуальные базовые классы инициализируются в порядке они появляются на глубине первого обхода слева направо направленного ациклического графа c базовых классов, где «слева направо» - порядок появления базовых классов в производном классе list.
Таким образом, виртуальный базовый класс должен быть создан по умолчанию, потому что его нет в mem-initializer-list наследующего конструктора BBC
. Но A
не имеет конструктора по умолчанию, поэтому он терпит неудачу (добавление A()=default;
, очевидно, может заставить его скомпилироваться, но здесь дело не в этом).
Но мне пока не ясно, почему BC
не делает нет этой проблемы? По сути это тот же пример, который дан cppreference в разделе Наследование конструкторов . Так что это должно работать. Но не выглядит ли это противоречащим стандарту? Когда B
наследует конструкторы от A
, он также получает ничего, кроме одного, отличного от значения по умолчанию, которое выполняет ту же инициализацию, что и определенная в BB
, за исключением неявного. Тогда, когда этот конструктор из B
наследуется от BC
, разве не должно применяться то же правило, где A
будет сконструировано по умолчанию, а значит, не скомпилировано?
Редактировать: @ j6t указал, что я смотрю на устаревший стандартный черновик. новый действительно лучше соответствует странице cppreference, которую я обнаружил ранее.
Одна вещь, которая остается для меня неясной, это то, что она объясняет, что должно произойти, если выбран виртуальный базовый конструктор, но как это наследуется внуком класса BC
в первую очередь? Из того же draft кажется, что виртуальный базовый конструктор, введенный using
, будет рассматриваться только в производном классе (B
в данном случае). Как using
в BC
наследует конструктор, который на два уровня выше?
Я ожидал бы, что когда BC
использует-объявляет конструкторы, то, что изначально унаследовано B
от A
, должно сначала обрабатываться как B
конструкторы, а затем наследоваться BC
, Но это не тот случай.