如何创建许多类来充当接口类的实现者,同时尽可能避免v-table成本,并且仍然允许静态转换到接口?
对于一个简单的案例,可以像下面的例子那样实现。
图书馆代码: -
updateData()
用户代码: -
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();
}
};
以下代码无法编译,但它描绘了我的梦想。 (full demo)。
图书馆代码: -
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)
}
用户代码: -
想要使用上述库的人可以轻松选择任何&#34;路由&#34;他想要。
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>
}并使用final手动实施RouterI1U2<User>
或i2()
或RouterI2U2<User>
}并使用final手动实施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)
}
它有效(demo),但模块化程度较低。 (低可用性)
我必须手动将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
。
答案 0 :(得分:2)
它可能不适用于您的情况,但也可能对其他人也有用。
我建议你在这个特殊情况下使用概念模型成语。这样做的目的是将多态实现和这些类本身的实现分成不同的部分。在这里,I
成为具有i1
和i2
成员函数的任何类的多态包装:
class I {
// The interface is internal, invisible to outside
// We use this as a type erasure technique and polymorphism
struct Concept {
virtual void i1() = 0;
virtual void i2() = 0;
};
// The single implementation that directly
// extend the interface is the model. T is the user class.
// T must have i1 and i2 function, because we call them.
template<typename T>
struct Model : Concept {
// The user class.
// If you want, you can use a reference there if you
// need references semantics with I
T user;
Model (T u) : user{std::move(u)} {}
// The only implementation of i1 is to call i1 from the user class
void i1() override {
user.i1();
}
void i2() override {
user.i2();
}
};
// Or use a shared, or use SBO
std::unique_ptr<Concept> concept;
public:
// When we make an I, we must provide a user class.
// If the user class had i1 and i2, it will compile.
// If Model takes a reference, use a reference there too.
template<typename T>
I(T model) : concept{std::make_unique<Model<T>>(std::move(model))} {}
void i1() {
concept->i1();
}
void i2() {
concept->i2();
}
};
然后,提供实现的类就像这样:
template<class Derived>
struct RouterI1U1 { // no Inheritance needed
void i1() { static_cast<Derived*>(this)->u1(); }
};
template<class Derived>
struct RouterI1U2 {
void i1() { static_cast<Derived*>(this)->u2(); }
};
template<class Derived>
struct RouterI2U1 {
void i2() { static_cast<Derived*>(this)->u1(); }
};
template<class Derived>
struct RouterI2U2 {
void i2() { static_cast<Derived*>(this)->u2(); }
};
由于这些i1
和i2
只需要&#34;那么&#34;为了适应Model<T>
类,不需要覆盖,因此不需要虚拟继承。
使用时会看起来像这样:
struct User : RouterI2U2<User> {
void i1() {}
void u2() {}
};
如您所见,我们没有任何虚拟方法。多态性是I
的实现细节。由于此类具有所有必需的成员函数,因此I
类将允许它。
使用班级I
也很简单。让User2
成为符合I
要求的另一个用户类:
User2 user2;
user2.i1(); // no vtable, so no vtable overhead possible
I myI{user2}; // works!
myI.i2(); // calls u2, with vtable
std::vector<I> v;
v.emplace_back(User2{});
v.emplace_back(User{}); // simple heh?
以下是如何删除路由器类,并使用&#34;或&#34;来实现此功能。风格界面。我的意思是一个接口,允许您实现或其他东西。
在Model<T>
课程中,您可以检查i1
和i2
是否存在。如果不存在,您可以提供调用u1
和u2
的实现。
我们首先制作类型特征,告诉我们特定类型T
是否具有成员函数i1
或i2
:
template<typename...>
using void_t = void;
template<typename, typename>
struct has_i1 : std::false_type {};
template<typename T>
struct has_i1<T, void_t<decltype(std::declval<T>().i1())>> : std::true_type {};
template<typename, typename>
struct has_i2 : std::false_type {};
template<typename T>
struct has_i2<T, void_t<decltype(std::declval<T>().i2())>> : std::true_type {};
现在,如果u1
和u2
不存在,我们可以将模型实施更改为调用i1
或i2
:
template<typename T>
struct Model : Concept {
T user;
Model(T u) : user{std::move(u)} {}
void i1() override {
i1_helper(user);
}
void i2() override {
i2_helper(user);
}
private:
template<typename U>
auto i1_helper(U& u) -> std::enable_if_t<has_i1<U>::value> {
// Call i1 if has i1
u.i1();
}
template<typename U>
auto i1_helper(U& u) -> std::enable_if_t<!has_i1<U>::value> {
// Call u1 if has not i1
u.u1();
}
template<typename U>
auto i2_helper(U& u) -> std::enable_if_t<has_i2<U>::value> {
// Call i2 if has i2
u.i2();
}
template<typename U>
auto i2_helper(U& u) -> std::enable_if_t<!has_i2<U>::value> {
// Call u2 if has not i2
u.u2();
}
};
现在,您的用户类是最简单的。
struct User1 {
void i1() {}
void i2() {}
};
struct User2 {
void i1() {}
void u2() {}
};
struct User3 {
void u1() {}
void i2() {}
};
struct User4 {
void u1() {}
void u2() {}
};
答案 1 :(得分:1)
使用虚拟继承。
template<class Derived>class RouterI1U1 : public virtual I{
等。使代码可编辑。