Каковы конкретные проблемы, которых избегают с ограничениями, налагаемыми этим пунктом 12.7p3
(см. первую часть параграфа ниже)?
В примере, показанном в 12.7p3
(см. ниже) почему X(this)
считается определенным? Это потому что X
не в пути E C D B A
?
struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X {
E() : D(this), // undefined: upcast from E* to A*
// might use path E* - D* - A*
// but D is not constructed
// D((C*)this), // defined:
// E* - C* defined because E() has started
// and C* - A* defined because
// C fully constructed
X(this) { // defined: upon construction of X,
// C/B/D/A sublattice is fully constructed
}
};
Пожалуйста, найдите ниже начала абзаца 12.7p3
:
Чтобы явно или неявно преобразовать указатель (glvalue), ссылающийся на
объект класса X на указатель (ссылку) на прямой или косвенный
Базовый класс B X, строительство X и строительство всех
его прямых или косвенных оснований, которые прямо или косвенно вытекают
из B должно начаться и уничтожение этих классов
не завершено, в противном случае преобразование приводит к неопределенному
поведение.
Правильно ли говорить, что совокупность всех прямых и косвенных оснований X
упомянутые выше не включают B
и из-за этого приведенный ниже код хорошо определен, несмотря на то, что Base
является прямой базой Derived
а еще не началось?
struct Base{ Base(Base*); };
struct Derived : Base {
Derived() : Base(this) {};
};
Каковы конкретные проблемы, которых следует избегать с помощью ограничений, налагаемых этим пунктом 12.7p3 (см. Первую часть параграфа ниже)?
Проблемы связаны с тем, что в какой-то момент виртуальные указатели (на виртуальную таблицу или виртуальные базовые классы) должны быть инициализированы для производного класса. Это инициализация, которая происходит во время «списка инициализации» (т. Е. Перед началом тела конструктора), обычно сразу после создания базового класса (например, после создания базового класса B, указателя виртуальной таблицы и, возможно, указателя виртуальной базы). устанавливается для класса X, а затем элементы данных инициализируются, а затем начинается тело конструктора). В общем, выполнение приведения перед инициализацией этих указателей приведет к неопределенному поведению, либо потому что требуемый поиск указателя не может быть выполнен для виртуальной базы, либо указатель виртуальной таблицы не будет установлен правильно для правильного вызова виртуальных функций ,
В примере, показанном в 12.7p3 (см. Ниже), почему X (это) считается определенным? Это потому, что X не на пути E C D B A?
Потому что конструкторы базового класса вызываются в том порядке, в котором они появляются в объявлении класса (т.е. struct E : C, D, X {
). Поскольку базовые классы C и D созданы, их общий виртуальный базовый класс A также определенно создан. И потому что X
не наследуется от класса A
, это означает, что существует полностью составленный и однозначный путь при выполнении приведения из класса E
к классу A
с этой точки зрения. Вот почему эта линия четко определена.
Правильно ли говорить, что набор всех прямых и косвенных оснований X, упомянутых выше, не включает B, и поэтому код ниже хорошо определен, несмотря на тот факт, что Base является прямой базой Derived и еще не начато?
Я не уверен, что следую вашему объяснению, но могу вам сказать, что код, который вы показали, не является четко определенным. При вызове конструктора Base
отливкой Derived* this
в Base*
объект базового класса в производном классе еще не создан, и, следовательно, приведение не определено.
Единственный способ, которым этот код мог бы иметь смысл, — это если бы в иерархии не было виртуальных функций или виртуальных базовых классов, и в этом случае такой код не понадобился бы, так как this
указатель на базовый класс уже неявно отправлен в конструктор базового класса.
Других решений пока нет …