Является ли это downcasting undefined?

Рассмотрим этот пример, в котором базовый класс имеет некоторые члены данных, а производный один предоставляет только дополнительный метод:

struct TestBase
{
    int x;
    TestBase() : x(5) {}
};

struct TestDerived : public TestBase
{
    void myMethod()
    {
        x=8;
    }
};

int main()
{
    TestBase b;
    TestDerived& d=static_cast<TestDerived&>(b);
    d.myMethod();
}

Это понижение до неправильного типа, поэтому AFAIU имеет поведение undefined. Но могут ли быть некоторые исключения для таких случаев, как это, где макет производного класса идентичен шаблону базового класса?

Ответ 1

Из стандартного (акцент мой):

§5.2.9 Статический литье [expr.static.cast]...

(2) lvalue типа "cv1 B" , где B - тип класса, может быть нажата для ввода "ссылки на cv2 D",, где D - это класс, полученный из B, если действительный стандартное преобразование из "указателя на D" в "указатель на B" существует, cv2 является такая же cv-квалификация, как или более высокая cv-квалификация, чем cv1 и B не является ни виртуальным базовым классом D, ни базовым классом виртуальной базы класс D. Результат имеет тип "cv2 D." Х-значение типа "cv1 B" может быть cast to type "rvalue reference to cv2 D" с теми же ограничениями, что и для lvalue типа "cv1 B." Если объект типа "cv1 B" на самом деле является подобъектом объекта тип D, результат относится к охватывающему объекту типа D. В противном случае поведение undefined.

Мое первое предположение заключалось в том, что приведение должно быть действительным в этом случае, потому что меня смутил термин подобъект.

Теперь (спасибо @T.C. и @M.M), очевидно, что в этом случае поведение undefined.

Листинг будет действительным в следующем примере:

int main()
{
    TestDerived d;
    TestBase &br = d; // reference to a subobject of d
    TestDerived &dr = static_cast<TestDerived&>(br);  // reference to the original d object
    d.myMethod();
}

Здесь объект класса TestDerived (d) будет иметь подобъект класса TestBase (br является ссылкой на этот объект).

Ответ 2

static_cast<> создаст ошибку компилятора, если вы попытаетесь выполнить бросок между несовместимыми типами, но не делает никаких заверений во время компиляции или времени выполнения, что акты действительны.

Так как TestDerived наследует от TestBase, это законное действие, разрешенное оператором static_cast, но не все downcasts обязательно являются безопасными нажатиями.

В приведенном выше коде просто работает безопасно - скорее всего, потому, что TestMethod только обращается к членам базового класса, является единственным наследованием, нет vtables и не делает ничего сложного. Таким образом, компилятор, скорее всего, обрабатывает приведение как простой пример. Другие скажут вам, что "это поведение undefined" - и не предполагать ничего о коде, написанном так. (И они тоже будут верны.)