Навіть, допустимо, ви чудово знаєте свою платформу, та добре знаєте, як у вас реалізується layout, vtables (і їх розміщення), paddings, etc. То, ви можете не вистрілити собі ніде в ногу і це навіть працюватиме стабільно, але є ще один підводний камінь, що якщо після розіменування B** ми можемо отримати B*, який вказує вже не на потрібний нам derived, а на якогось іншого спадкоємця?
#include <iostream>
struct B {
virtual ~B() {}
virtual void print() = 0;
};
struct D : B {
int i;
void print() override { std::cout << "[D::print] j = " << i << '\n'; }
virtual void do_nothing() { std::cout << "[D::do_nothing]\n"; }
};
struct XXX : B {
void print() override { }
virtual void hack() { std::cout << "[XXX::hack]\n"; }
};
int main() {
D* dparr[2]{new D, new D};
dparr[1]->do_nothing();
B** bpp = (B**)dparr; // same as reinterpret_cast<B**>(dparr)
XXX hacker;
bpp[1] = &hacker;
dparr[1]->do_nothing(); // calls XXX::hack
}
https://rextester.com/UTZEY51315
mimik написав:Я тепер нічого не розумію.
Коротше кажучи, на більшості сучасних платформ та компіляторів код з https://rextester.com/IKSXJW90373 - працюватиме, бо загалом розробники компіляторів теж не дурні, а на більшості систем layout буде очікуваним (тобто у вас UB лише формально, на ділі всеодно все працюватиме), але якщо ви пишете, наприклад, бібліотеку, якою можуть користуватися хтозна де, то боронь Боже вас такий код писати)