https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81078
--- Comment #1 from Arthur O'Dwyer <arthur.j.odwyer at gmail dot com> --- I've found that the misbehavior actually depends on the *ordering* of bases in the class graph, not just the shape of the graph. // https://wandbox.org/permlink/DaKQxTc5Ldqj9FlY #include <stdio.h> struct Sideboard { virtual ~Sideboard() {} }; struct Shared { virtual ~Shared() {} }; struct Public : public virtual Shared {}; struct Protected : protected virtual Shared {}; template<class... Ts> struct Main : Ts... {}; template<class... Ts> void test() { Main<Ts...> m; Sideboard *sb = &m; Shared *sh = &m; if (dynamic_cast<Shared*>(sb) != sh) { puts(__PRETTY_FUNCTION__); } } int main() { test<Protected, Public, Sideboard>(); test<Protected, Sideboard, Public>(); test<Public, Protected, Sideboard>(); test<Public, Sideboard, Protected>(); test<Sideboard, Protected, Public>(); test<Sideboard, Public, Protected>(); } OUTPUT is: void test() [with Ts = {Public, Protected, Sideboard}] That is, the failure case seems to be when (and only when): - the base being casted "from" is laid out after any base descended from "to", AND - the last base (in lexical order) descended from "to" is not publicly descended from "to".