classAClosure : public BaseClosure { public: virtualvoidf(){ puts("B"); } };
classBClosure : public AClosure { };
// CL is the concrete type, set by the caller of `iterate`. // U is the root type in the type hierarchy; `BaseClosure` in this example. // T is between [CL, U]; the first type with a `void f()` method, searching from `CL` to `U`. classO { #if 0 template<typename T, typename U, typename CL> void iterate(void (T::*)(), void (U::*)(), CL* t){ t->f(); } #else template<typename T, typename U, typename CL> typename enable_if<is_same<T, U>::value, void>::type iterate(void (T::*)(), void (U::*)(), CL* t){ t->f(); } template<typename T, typename U, typename CL> typename enable_if<!is_same<T, U>::value, void>::type iterate(void (T::*)(), void (U::*)(), CL* t){ t->T::f(); } #endif public: template<typename CL> voiditerate(CL* t){ iterate(&CL::f, &BaseClosure::f, t); } };
voidmyfunc(BClosure* cl){ O o; o.iterate(cl); }
Using the #if switch, we can control the devirtulation.
Comparing slow.s with fast.s, we can see the key difference lies in the body of myfunc. In the fast case, puts is inlined, hence a direct
call, in contrast to the indirect call in the slow case.