Proč tak složitě? Stačí
((bar*)(obj))->b();
Protože to není totéž. static_cast umí zařídit i patřičný offset u vícenásobné dědičnosti:
V tomto případě bych použil dynamic_cast - ten je přesně na toto dělaný. Za běhu zkontroluje typ (je tedy o trochu pomalejší než static_cast) a pokud by neseděl (tj. jeden objekt by nebyl potomkem druhého), vrátí NULL.
dynamic_cast je dobré používat tam, kde opravdu nemám jistotu, že to co mi přilezlo ukazatelem je skutečně objekt, který očekávám. dynamic_cast má tyto nevýhody
- Lze použít pouze na objekty, které mají nějaké virtuální funkce (stačí destruktor)
- Nelze jej použít jako reinterpret_cast, tedy objekt opravdu musí být tím, co očekáváme (někdy se hodí umět to přetypovat navzdory)
- Vyžaduje akci v runtime. Zatímco všechny XXX_cast operátory vyhodnocuje překladač, dynamic_cast se vyhodnocuje za běhu. A není to zrovna levná záležitost.
Většinou se to řeší tak, že v tabulce virtuálních metod je přibalen ukazatel na záznam o třídě objektu a ten obsahuje odkaz na tabulku všech tříd, které dědí a to i tranzitivně. Operátor pak projde tabulku a nalezne v ní třídu ukazatele a cílovou třídu a spočítá transformaci výsledného pointeru... tedy spočítá jeho offset. Jako klíč do tabulky bývá zakódované jméno třídy (porovnávají se řetězce) - Microsoft, případně nějaké GUID třídy (COM+, prý to používá i gcc)
Nicméně, pokud vím, že mi do nějaké funkce může přilézt objekt jen určitého potomka k danému rozhraní, nebo základní třídy, tak používám static_cast, je to rychlejší. Je však dobré dynamic_cast ověřit aspoň pomocí _assertu v testovací verzi.
PS: Nevýhoda static_castu: nedá se použít na virtuální dědičnost.