基于标志的多重继承
Multiple inheritance based on flags
我有几个class,比如A
、B
和C
,以及相应的标志HAS_A=1
、HAS_B=2
,和 HAS_C=4
。是否可以这样写 class,使其 parents(来自 A
、B
和 C
)将由以下组合确定这些旗帜?
示例:
ParentsFromFlags<HAS_A | HAS_C> x;
// x ends up having the features of A and C
我知道我可以有可变倍数 parents 和 <typename... Parents>
,但我喜欢这个,因为我想确保如果 A
、B
和C
是 class 的 parents,它们总是以一定的顺序出现。
这样就可以了,代价是在层次结构中引入一些额外的 class...
enum ParentFlags { HAS_A = 1, HAS_B = 2, HAS_C = 4 };
class A{};
class B{};
class C{};
template <int M>
class ParentClass {};
template <>
class ParentClass<0>{};
template <>
class ParentClass<HAS_A> : public A {};
template <>
class ParentClass<HAS_B> : public B{};
template <>
class ParentClass<HAS_C> : public C{};
template <int F, int M>
class ParentTraits : public ParentClass<F & M>,
public ParentTraits<F & ~M, M << 1>
{};
template <int M>
class ParentTraits<0, M>
{};
template <int F>
class ParentFromFlags : public ParentTraits<F, 1>
{
};
int main()
{
ParentFromFlags<HAS_A | HAS_B> ab;
ParentFromFlags<HAS_A | HAS_C> ac;
ParentFromFlags<HAS_A | HAS_B | HAS_C> abc;
return 0;
}
这比您正在寻找的更通用一些,但您现在需要的只是用 filtered_list
替换元函数,根据您的标志过滤 类 .
template<typename... T>
struct type_list;
template<typename T>
struct filtered_list;
template<typename T, typename... U>
struct filtered_list<type_list<T,U...>> {
using type = type_list<U...>;
};
template<typename TypeList>
using filtered_list_t = typename filtered_list<TypeList>::type;
template<typename T>
struct collect_base_classes;
template<typename... T>
struct collect_base_classes<type_list<T...>> : public T... {};
struct A { void test_a() {} };
struct B { void test_b() {} };
struct C { void test_c() {} };
class Test : public collect_base_classes<filtered_list_t<type_list<A,B,C>>> {};
int main() {
Test t;
t.test_a(); //error, we dropped A from our base class list
t.test_b();
t.test_c();
}
有一个简单的方法和一个最优的方法来解决这个问题。
最简单的解决方案是只使用像 conditional_t 这样的编译时类型选择器,结合空基 class:
template <int M>
struct empty_base {};
template <int flags>
struct Foo
: std::conditional_t<flags & Has_A, A, empty_base<1>>
, std::conditional_t<flags & Has_B, B, empty_base<2>>
, std::conditional_t<flags & Has_C, C, empty_base<3>>
{
int x;
};
这种方法的问题在于,由于使用了多重继承,它无法触发 C++ 中的 empty base class optimization。因此,Foo 值将比必要的大一个字。
您可以通过以类似于 boost.compressed_pair 的方式链接碱基来解决此问题:
template <class T1, class T2>
struct compressed_pair_of_bases: T1, T2 {};
template <class T1, int N>
struct compressed_pair_of_bases<T1, empty_base<N>>: T1 {};
template <bool Predicate, class T, class Next>
using predicated_parent_chain_t = typename std::conditional_t<Predicate,
compressed_pair_of_bases<T, Next>,
Next>;
template <int flags>
struct Bar :
predicated_parent_chain_t<!!(flags & Has_A), A,
predicated_parent_chain_t<!!(flags & Has_B), B,
predicated_parent_chain_t<!!(flags & Has_C), C,
empty_base<1>>>>
{
int x;
};
此解决方案能够在未选择时完全优化掉基本类型:
std::cout << sizeof(Bar<0>); // prints 4 on a 32-bit target
实际上这就是我最终得到的。它与 基本相同,但更具可读性(至少对我而言)
enum ParentFlags { HAS_A = 1, HAS_B = 2, HAS_C = 4 };
class A { int a; };
class B { int b[2]; };
class C { int c[3]; };
template <int Flags>
class ParentA {};
template <>
class ParentA<HAS_A> : public A
{};
template <int Flags, int ThisFlag = Flags & HAS_B>
class ParentB : public ParentA<Flags & ~HAS_B>
{};
template <int Flags>
class ParentB<Flags, HAS_B> : public ParentA<Flags & ~HAS_B>, public B
{};
template <int Flags, int ThisFlag = Flags & HAS_C>
class ParentC : public ParentB<Flags & ~HAS_C>
{};
template <int Flags>
class ParentC<Flags, HAS_C> : public ParentB<Flags & ~HAS_C>, public C
{
};
template <int Flags>
class ParentFromFlags : public ParentC<Flags>
{};
我有几个class,比如A
、B
和C
,以及相应的标志HAS_A=1
、HAS_B=2
,和 HAS_C=4
。是否可以这样写 class,使其 parents(来自 A
、B
和 C
)将由以下组合确定这些旗帜?
示例:
ParentsFromFlags<HAS_A | HAS_C> x;
// x ends up having the features of A and C
我知道我可以有可变倍数 parents 和 <typename... Parents>
,但我喜欢这个,因为我想确保如果 A
、B
和C
是 class 的 parents,它们总是以一定的顺序出现。
这样就可以了,代价是在层次结构中引入一些额外的 class...
enum ParentFlags { HAS_A = 1, HAS_B = 2, HAS_C = 4 };
class A{};
class B{};
class C{};
template <int M>
class ParentClass {};
template <>
class ParentClass<0>{};
template <>
class ParentClass<HAS_A> : public A {};
template <>
class ParentClass<HAS_B> : public B{};
template <>
class ParentClass<HAS_C> : public C{};
template <int F, int M>
class ParentTraits : public ParentClass<F & M>,
public ParentTraits<F & ~M, M << 1>
{};
template <int M>
class ParentTraits<0, M>
{};
template <int F>
class ParentFromFlags : public ParentTraits<F, 1>
{
};
int main()
{
ParentFromFlags<HAS_A | HAS_B> ab;
ParentFromFlags<HAS_A | HAS_C> ac;
ParentFromFlags<HAS_A | HAS_B | HAS_C> abc;
return 0;
}
这比您正在寻找的更通用一些,但您现在需要的只是用 filtered_list
替换元函数,根据您的标志过滤 类 .
template<typename... T>
struct type_list;
template<typename T>
struct filtered_list;
template<typename T, typename... U>
struct filtered_list<type_list<T,U...>> {
using type = type_list<U...>;
};
template<typename TypeList>
using filtered_list_t = typename filtered_list<TypeList>::type;
template<typename T>
struct collect_base_classes;
template<typename... T>
struct collect_base_classes<type_list<T...>> : public T... {};
struct A { void test_a() {} };
struct B { void test_b() {} };
struct C { void test_c() {} };
class Test : public collect_base_classes<filtered_list_t<type_list<A,B,C>>> {};
int main() {
Test t;
t.test_a(); //error, we dropped A from our base class list
t.test_b();
t.test_c();
}
有一个简单的方法和一个最优的方法来解决这个问题。
最简单的解决方案是只使用像 conditional_t 这样的编译时类型选择器,结合空基 class:
template <int M>
struct empty_base {};
template <int flags>
struct Foo
: std::conditional_t<flags & Has_A, A, empty_base<1>>
, std::conditional_t<flags & Has_B, B, empty_base<2>>
, std::conditional_t<flags & Has_C, C, empty_base<3>>
{
int x;
};
这种方法的问题在于,由于使用了多重继承,它无法触发 C++ 中的 empty base class optimization。因此,Foo 值将比必要的大一个字。
您可以通过以类似于 boost.compressed_pair 的方式链接碱基来解决此问题:
template <class T1, class T2>
struct compressed_pair_of_bases: T1, T2 {};
template <class T1, int N>
struct compressed_pair_of_bases<T1, empty_base<N>>: T1 {};
template <bool Predicate, class T, class Next>
using predicated_parent_chain_t = typename std::conditional_t<Predicate,
compressed_pair_of_bases<T, Next>,
Next>;
template <int flags>
struct Bar :
predicated_parent_chain_t<!!(flags & Has_A), A,
predicated_parent_chain_t<!!(flags & Has_B), B,
predicated_parent_chain_t<!!(flags & Has_C), C,
empty_base<1>>>>
{
int x;
};
此解决方案能够在未选择时完全优化掉基本类型:
std::cout << sizeof(Bar<0>); // prints 4 on a 32-bit target
实际上这就是我最终得到的。它与
enum ParentFlags { HAS_A = 1, HAS_B = 2, HAS_C = 4 };
class A { int a; };
class B { int b[2]; };
class C { int c[3]; };
template <int Flags>
class ParentA {};
template <>
class ParentA<HAS_A> : public A
{};
template <int Flags, int ThisFlag = Flags & HAS_B>
class ParentB : public ParentA<Flags & ~HAS_B>
{};
template <int Flags>
class ParentB<Flags, HAS_B> : public ParentA<Flags & ~HAS_B>, public B
{};
template <int Flags, int ThisFlag = Flags & HAS_C>
class ParentC : public ParentB<Flags & ~HAS_C>
{};
template <int Flags>
class ParentC<Flags, HAS_C> : public ParentB<Flags & ~HAS_C>, public C
{
};
template <int Flags>
class ParentFromFlags : public ParentC<Flags>
{};