如何创建多个类来作为接口类的实现者,同时尽可能避免虚表成本,仍然能够进行静态转换?
对于简单情况,可以像下面的示例一样实现。
示例
库代码 :-
class I{ //interface
public: virtual void i1()=0;
};
template<class Derived>class Router : public I{
public: virtual void i1()final{
//in real case it is very complex, but in the core is calling :-
static_cast<Derived*>(this)->u1();
}
};
用户代码 :-
class User : public Router<User>{
public: void u1(){ std::cout<<"hi"<<std::endl; }
};
int main() {
User u;
u.i1(); //<-- no v-table cost
I* i=&u;
i->i1(); //<-- has v-table cost (OK)
}
问题
下面的代码无法编译,但这是我的梦想。(完整演示)。
库代码 :-
class I{ //interface
public: virtual void i1()=0;
public: virtual void i2()=0;
};
template<class Derived>class RouterI1U1 : public I{
public: virtual void i1()final{ static_cast<Derived*>(this)->u1(); }
};
template<class Derived>class RouterI1U2 : public I{
public: virtual void i1()final{ static_cast<Derived*>(this)->u2(); }
};
template<class Derived>class RouterI2U1 : public I{
public: virtual void i2()final{ static_cast<Derived*>(this)->u1(); }
};
template<class Derived>class RouterI2U2 : public I{
public: virtual void i2()final{ static_cast<Derived*>(this)->u2(); }
};
用户代码 :-
想要使用以上库的人,可以轻松地选择任何他想要的“路线”。
- 派生自
RouterI1U2<User>
和RouterI2U1<User>
或 - 派生自
RouterI1U1<User>
和RouterI2U2<User>
或 - 派生自{
RouterI1U1<User>
或RouterI1U2<User>
}并手动实现i2()
,或 - 派生自{
RouterI2U2<User>
或RouterI2U1<User>
}并手动实现i1()
,或 - 手动实现
i1()
和i2()
以下是一个例子。
class User : public RouterI1U2<User>,public RouterI2U1<User>{
public: void u1(){ std::cout<<"hi1"<<std::endl; }
public: void u2(){ std::cout<<"hi2"<<std::endl; }
};
int main() {
User u;
u.i1(); //<-- no v-table cost
I* i=&u;
i->i1(); //<-- has v-table cost (OK)
}
我不太好的解决方案
class I{ //interface
public: virtual void i1()=0;
public: virtual void i2()=0;
};
template<class Derived> class RouterI1U2_I2U1 : public I{ //group it
public: virtual void i1()final{ static_cast<Derived*>(this)->u2(); }
public: virtual void i2()final{ static_cast<Derived*>(this)->u1(); }
};
class User : public RouterI1U2_I2U1<User>{
public: void u1(){ std::cout<<"hi1"<<std::endl; }
public: void u2(){ std::cout<<"hi2"<<std::endl; }
};
它可以工作(演示),但提供的模块性较少。(低重用性)
我必须手动打包RouterI1U2
和RouterI2U1
到RouterI1U2_I2U1
中。
void i1() { static_cast<Derived*>(this)->u1(); }
的重复。在我的活动项目中,我有一个bool update(Updatable<T> &u)
,它调用了static_cast
-ed u的update
方法,而Updatable<T>
只是实现update()
的承诺。如果这个承诺没有被遵守,就会导致替换失败,并且您可以决定默认候选项或编译器错误更合适。一旦您掌握了它,请尝试嵌套/链接/common_type
/等等! - John P