#include #include struct Base { virtual int f() = 0; }; struct Derived1 : public Base { virtual int f() final override { return 42; } }; struct Derived2 : public Base { virtual int f() final override { return 13; } }; template struct pmf_traits; template struct pmf_traits { using self_type = R (T::*)(Ps...); using class_type = T; using result_type = R; using func_type = R (Ps...); }; // ^^ repeat for cv-qualifiers template struct devirt_caller; template struct devirt_caller { using T = typename pmf_traits::class_type; using R = typename pmf_traits::result_type; template static R call(T& o, Ps&& ...params) { return (o.*pmf)(std::forward(params)...); } }; template struct devirt_caller { using T = typename pmf_traits::class_type; using R = typename pmf_traits::result_type; template static R call(T& o, Ps&& ...params) { if (typeid(o) == typeid(D)) { auto ptr = static_cast(&o); const auto rpmf = static_cast(pmf); return (ptr->*rpmf)(std::forward(params)...); } return devirt_caller::call(o, std::forward(params)...); } }; template struct devirt { template using with = devirt_caller; }; #define make_devirt(a) devirt int test(Base& b) { return make_devirt(&Base::f)::with::call(b); } // Above should statically unfold to the following (but doesn't) int test2(Base& b) { if (typeid(b) == typeid(Derived1)) return static_cast(&b)->f(); if (typeid(b) == typeid(Derived2)) return static_cast(&b)->f(); return b.f(); }