multiple inheritance for function only - without virtual and CRTP
如何只为函数进行多重继承?
- 必须共享基类的数据
- 无虚拟功能(假设vtable很贵)
- 避免虚拟继承
- 实现必须能够驻留在.cpp中
- 允许C++ 14
以下是类似的问题:
- 菱形形状中的多个继承仅具有函数-使用虚拟继承。虚拟继承通常是不好的和昂贵的。
- 无虚拟继承的多重继承-侧重于语法和编译,而不是编程技术。
- 在C++(CRTP)、CRTP和多级继承中的多级继承,用CRTP和多重继承(C++ 03)消除冗余,并使用具有虚拟继承的CRTP实现。
下面是一个示例代码(coliru demo):-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class O{ protected: int database=0; }; class A : public O{ public: void print(){ std::cout<<database<<std::endl; } }; class B : public O{ public: void set(int s){ database=s+1; } }; class AB : public O{ public: void print(){//duplicate std::cout<<database<<std::endl; } public: void set(int s){//duplicate database=s+1; } }; //AB ab; ab.set(1); ab.print(); // would print 2 |
这是我的尝试(魔盒演示)。我滥用crtp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class O{ public: int database=0; }; template<class T>class OA{ public: void print(){ std::cout<<static_cast<T*>(this)->database<<std::endl; } }; template<class T>class OB{ public: void set(int s){ static_cast<T*>(this)->database=s+1; } }; class A :public O,public OA<A>{}; class B :public O,public OB{}; class AB :public O,public OA<AB>,public OB<AB>{}; |
它能用,但看起来不漂亮。此外,实现必须在头文件中(因为
有更好的方法吗?还是这样?
对不起,如果这是太新的问题或已经问了。我是一个C++初学者。
编辑Give extended example of using please.
在ECS中,它在某些情况下很有用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class O{ protected: EntityHandle e; }; class ViewAsPhysic : public O{ //A public: void setTransform(Transformation t){ Ptr<PhysicTransformComponent> g=e; g->transform=t; } }; class ViewAsLight : public O{ //B public: void setBrightness(int t){ Ptr<LightComponent> g=e; g->clan=t; } }; class ViewAsLightBlock : public O{ //AB //both functions }; |
我们从定义可以打印的东西和可以设置的东西开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | namespace util { template<class Base, class Derived, class R=void> using if_base = std::enable_if_t< std::is_base_of< std::decay_t<Base>, std::decay_t<Derived>>::value, R >; struct stub {}; } namespace concepts { template<class Token> void do_print(Token, util::stub const&)=delete; template<class Token> void do_set(Token, util::stub&, int)=delete; struct has_print { struct token { friend struct has_print; private: token(int){} }; template<class T> friend util::if_base<has_print, T> print(T const& t) { do_print(get_token(), t); } private: static token get_token() { return 0; } }; struct has_set { struct token { friend struct has_set; private: token(int){} }; template<class T> friend util::if_base<has_set, T> set(T& t, int x) { do_set(get_token(),t, x); } private: static token get_token() { return 0; } }; } |
然后我们宣布
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace DB { class O; void do_print(::concepts::has_print::token, O const& o); void do_set(::concepts::has_set::token, O& o, int); class O{ protected: int database=0; friend void do_print(::concepts::has_print::token, O const&); friend void do_set(::concepts::has_set::token, O&, int); }; class A : public O, public concepts::has_print { }; class B : public O, public concepts::has_set { }; class AB : public O, public concepts::has_print, concepts::has_set { }; } void DB::do_print(::concepts::has_print::token, O const& o ) { std::cout << o.database << std::endl; } void DB::do_set(::concepts::has_set::token, O& o, int x) { o.database = x+1; } |
其中最困难的部分是访问控制。
我保证不可能打电话给
这就是所有这些
活生生的例子。
这里的问题是,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include <iostream> class O{ int _db; protected: int &database; O(): database(_db) {}; O(int &db): database(db) {}; }; class A : public O{ public: void print(){ std::cout<<database<<std::endl; } A() {} // public default ctor protected: A(int& db): O(db) {}; // protectect ctor }; class B : public O{ public: void set(int s){ database=s+1; } B() {} // public default ctor protected: B(int& db): O(db) {}; // protectect ctor }; class AB : public A, public B { int _db2; public: AB(): A(_db2), B(_db2) {}; // initialize both references to same private var }; int main() { AB ab; ab.set(1); ab.print(); return 0; } |
按预期显示:
1 | 2 |
上面的代码不使用虚拟继承、虚拟函数和模板,因此方法可以安全地在cpp文件中实现。AB类实际上使用它的两个父类的方法,并且仍然对其基础数据有一个一致的视图。实际上,它通过在最派生类中构建公共数据并通过其父类中受保护的构造函数注入来模拟显式虚拟继承。
像这样?
- 必须共享基类的数据-检查
- 无虚拟功能(假设vtable很贵)-检查
- 避免虚拟继承-检查
- 实现必须能够驻留在.cpp-check中
- 允许C++ 14检查。使用C++ 11。
nbsp;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <iostream> class O { protected: int database = 0; }; /* * the concept of implementing print for a base class */ template<class...Bases> struct implements_print : Bases... { void print() const { std::cout << this->database << std::endl; } }; /* * The concept of implementing set for a base class */ template<class...Bases> struct implements_set : Bases... { void set() { ++this->database; } }; struct B : implements_set<O> { }; struct A : implements_print<O> { }; struct AB : implements_set<implements_print<O>> { }; int main() { A a; a.print(); B b; b.set(); AB ab; ab.set(); ab.print(); } |
另一种方法,使用组合和访问类提供对受保护成员的访问。这个例子演示了如何将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #include <iostream> /* * this stuff in cpp */ namespace implementation { void print(const int& database) { std::cout << database << std::endl; } void set(int& database) { ++database; } } /* * this stuff in header */ struct OAccess; class O { private: int database = 0; friend OAccess; }; struct OAccess { template<class Host> constexpr decltype(auto) database(Host &host) const { return (host.database); } // note: () makes reference template<class Host> constexpr decltype(auto) database(Host const &host) const { return (host.database); } // note: () makes reference }; /* * the concept of implementing print for a derived class */ template<class Host> struct implements_print { void print() const { OAccess access; implementation::print(access.database(self())); } private: decltype(auto) self() const { return static_cast<Host const &>(*this); } }; /* * The concept of implementing set for a derived class */ template<class Host> struct implements_set { void set() { OAccess access; implementation::set(access.database(self())); } private: decltype(auto) self() { return static_cast<Host &>(*this); } }; template<template<class> class...Impls> struct OImpl : Impls<OImpl<Impls...>> ..., O { }; using B = OImpl<implements_set>; using A = OImpl<implements_print>; using AB = OImpl<implements_print, implements_set>; int main() { A a; a.print(); B b; b.set(); AB ab; ab.set(); ab.print(); } |
开始讨论。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | class O { // no virtual destructor. So cant use polymorphic deletion // like : // O *o = new AB; // delete o; protected: int database=0; }; class A : virtual public O{ public: void print(){ std::cout<<database<<std::endl; } }; class B : virtual public O{ public: void set(int s){ database=s+1; } }; class AB : protected A, protected B{}; // no vtable void foo() { AB ab; ab.print(); // won't perform virtual call. } |