以下代码旨在为我的库提供有关用户类派生的基类的反射信息:
template <class Base1_ = void, class Base2_ = void, class Base3_ = void,
class Base4_ = void>
struct ManagedNode;
// For classes that do not derive
template <> struct ManagedNode<void, void, void, void> {
using Base1 = void; using Base2 = void; using Base3 = void;
using Base4 = void;
};
// To avoid inaccessible base
// See http://stackoverflow.com/q/34255802/2725810
struct Inter0: public ManagedNode<>{};
// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : public Inter0,
public Base1_ {
using Base1 = Base1_;
};
// To avoid inaccessible base
template <class Base1_>
struct Inter1: public ManagedNode<Base1_>{};
// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void> : public Inter1<Base1_>,
public Base2_ {
using Base2 = Base2_;
};
// We can continue in the same manner for 3 and 4 base classes
以下是一个示例用户代码:
struct A : public ManagedNode<> {
int data1;
};
struct B : public ManagedNode<> {};
struct C : public ManagedNode<A, B> {};
int main() {
C c;
std::cout << sizeof(c) << std::endl;
return 0;
}
此代码生成12的输出,这意味着c
包含data1
成员三次!
我考虑过使用虚拟继承来避免这种内存开销。如果我只是在每个继承的单词virtual
之前插入单词public
,那么我会收到关于基类变得无法访问的警告...如果我在每个这样的单词中加上单词virtual
除了Inter0
的声明之外,我得到16的输出 - 比以前更差!
我非常感谢在这里使用虚拟继承时会发生什么的解释。
编辑:为了完整性,这里插入了virtual
的版本(注释指示需要删除哪个virtual
以便编译代码):
template <class Base1_ = void, class Base2_ = void, class Base3_ = void,
class Base4_ = void>
struct ManagedNode;
// For classes that do not derive
template <> struct ManagedNode<void, void, void, void> {
using Base1 = void; using Base2 = void; using Base3 = void;
using Base4 = void;
};
// To avoid inaccessible base
// See http://stackoverflow.com/q/34255802/2725810
struct Inter0: virtual public ManagedNode<>{}; // without the word virtual
// in this line, the code compiles
// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : virtual public Inter0,
virtual public Base1_ {
using Base1 = Base1_;
};
// To avoid inaccessible base
template <class Base1_>
struct Inter1: virtual public ManagedNode<Base1_>{};
// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void> : virtual public Inter1<Base1_>,
virtual public Base2_ {
using Base2 = Base2_;
};
// Some user classes for testing the concept
struct A : public ManagedNode<> {
int data1;
};
struct B : public ManagedNode<> {};
struct C : public ManagedNode<A, B> {};
int main() {
C c;
std::cout << sizeof(c) << std::endl;
return 0;
}
以下是编译器的输出:
temp.cpp: In instantiation of ‘struct ManagedNode<A, void, void, void>’:
temp.cpp:27:8: required from ‘struct Inter1<A>’
temp.cpp:31:8: required from ‘struct ManagedNode<A, B>’
temp.cpp:44:19: required from here
temp.cpp:21:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘ManagedNode<A, void, void, void>’ due to ambiguity [-Werror=extra]
struct ManagedNode<Base1_, void, void, void> : virtual public Inter0,
^
temp.cpp: In instantiation of ‘struct Inter1<A>’:
temp.cpp:31:8: required from ‘struct ManagedNode<A, B>’
temp.cpp:44:19: required from here
temp.cpp:27:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘Inter1<A>’ due to ambiguity [-Werror=extra]
struct Inter1: virtual public ManagedNode<Base1_>{};
^
temp.cpp: In instantiation of ‘struct ManagedNode<A, B>’:
temp.cpp:44:19: required from here
temp.cpp:31:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘ManagedNode<A, B>’ due to ambiguity [-Werror=extra]
struct ManagedNode<Base1_, Base2_, void, void> : virtual public Inter1<Base1_>,
^
temp.cpp:44:8: error: virtual base ‘ManagedNode<void, void, void, void>’ inaccessible in ‘C’ due to ambiguity [-Werror=extra]
struct C : public ManagedNode<A, B> {};
^
答案 0 :(得分:2)
这意味着
C
包含data1
成员三次!
C
大于预期的原因并非如此。
问题是所有类都继承自ManagedNode<>
,因此每个对象必须具有唯一的地址和类型组合,因此在最终结构中添加了偏移量。
布局C:
- 0x00: ManagedNode<> // From Inter0
- 0x04: ManagedNode<> // From A
- 0x04: int // From A
- 0x08: ManagedNode<> // From B
注意:ManagedNode<>
为空。
答案 1 :(得分:0)
经过多次努力(见Jarod42的评论和答案),我提出了以下非常天真的解决方案。它有一些重复的代码,但避免了基类中含糊不清的所有麻烦:
// For classes that do not derive
template<> struct ManagedNode<void, void, void, void> {
using Base1 = void;
};
// For classes that derive from a single base class
template <class Base1_>
struct ManagedNode<Base1_, void, void, void> : public Base1_ {
using Base1 = Base1_;
using Base2 = void;
};
// For classes that derive from two base classes
template <class Base1_, class Base2_>
struct ManagedNode<Base1_, Base2_, void, void>
: public Base1_, public Base2_ {
using Base1 = Base1_;
using Base2 = Base2_;
using Base3 = void;
};