Мне интересно, приводит ли следующий код к неопределенному поведению:
#include <cstddef>
#include <cstdio>
struct IA {
virtual ~IA() {}
int a = 0;
};
struct IB {
virtual ~IB() {}
int b = 0;
};
struct C: IA, IB {};
int main() {
C* pc = nullptr;
IB* pib = pc;
std::printf("%p %p", (void*)pc, (void*)pib);
}
Страуструп обсуждает этот случай в разделе 4.5 его документ о множественном наследовании 1989 года [PDF]:
Решение заключается в разработке операции преобразования (приведения) в
проверить значение указателя 0 […]Дополнительная сложность и накладные расходы во время выполнения — это тест и
приращение.
Реализация явно проверяет наличие нулевых значений и гарантирует, что результатом приведения по-прежнему будет нулевое значение. Это было верно в C ++ 98 и не изменилось в C ++ 11 и nullptr
,
Это особенно важно в случае нескольких базовых классов, где приведение производного класса к одному из базовых классов может потребовать изменения фактического значения указателя.
В вашем примере макет C
в памяти будет сначала содержать байты для IA
сопровождаемый байтами для IB
, Кастинг в IA
является trival, как указатель на начало C
также будет указывать на начало IA
часть C
, Кастинг в IB
с другой стороны, требует сдвига C
указатель по размеру IA
, Выполнение этого сдвига в случае nullptr приведет к ненулевому указателю после приведения, следовательно, особая обработка для нулей.
Как указал Ашеплер, соответствующий раздел в стандарте — [conv.ptr] §4.10:
Значение типа «указатель на резюме
D
«, гдеD
это тип класса, может быть
преобразуется в значение типа «указатель на резюмеB
«, гдеB
это база
классD
, […] Результатом преобразования является указатель на
подобъект базового класса объекта производного класса. Нулевой указатель
значение преобразуется в значение нулевого указателя типа назначения.
Обновление нулевого указателя четко определено, чтобы дать вам еще один нулевой указатель:
4.10p3:
Значение типа «указатель на резюме
D
«, гдеD
является типом класса, может быть преобразован в значение типа «указатель на резюмеB
«, гдеB
это базовый классD
, … Значение нулевого указателя преобразуется в значение нулевого указателя целевого типа.