如何实现多条件继承?解决重复继承empty结构体编译错误
问题描述
我定义了一个带标志模板的build类型,通过激活的标志位继承对应类型,以此实现从多个子类构建大量配置的类。代码如下:
#include <type_traits> #include <cstdint> struct A { void a() {} }; struct B { void b() {} }; struct C { void c() {} }; struct D { void d() {} }; constexpr std::uint8_t FLAG_BIT_A = 0b1 << 0; constexpr std::uint8_t FLAG_BIT_B = 0b1 << 1; constexpr std::uint8_t FLAG_BIT_C = 0b1 << 2; constexpr std::uint8_t FLAG_BIT_D = 0b1 << 3; struct empty {}; template<std::uint8_t flags> using flag_a_type = std::conditional_t<(flags & FLAG_BIT_A), A, empty>; template<std::uint8_t flags> using flag_b_type = std::conditional_t<(flags & FLAG_BIT_B), B, empty>; template<std::uint8_t flags> using flag_c_type = std::conditional_t<(flags & FLAG_BIT_C), C, empty>; template<std::uint8_t flags> using flag_d_type = std::conditional_t<(flags & FLAG_BIT_D), D, empty>; template<std::uint8_t flags> struct build : flag_a_type<flags>, flag_b_type<flags>, flag_c_type<flags>, flag_d_type<flags> { }; int main() { build<FLAG_BIT_A | FLAG_BIT_C> foo; }
预期build<FLAG_BIT_A | FLAG_BIT_C>会继承A和C,但编译报错:
error C2500: 'build<5>': 'empty' is already a direct base class
需要在不定义4个不同empty结构体的前提下解决该问题。
解决方案1:给empty添加模板参数区分
把empty改成带模板参数的结构体,用不同参数生成不同空类型,避免重复直接基类的冲突:
#include <type_traits> #include <cstdint> struct A { void a() {} }; struct B { void b() {} }; struct C { void c() {} }; struct D { void d() {} }; constexpr std::uint8_t FLAG_BIT_A = 0b1 << 0; constexpr std::uint8_t FLAG_BIT_B = 0b1 << 1; constexpr std::uint8_t FLAG_BIT_C = 0b1 << 2; constexpr std::uint8_t FLAG_BIT_D = 0b1 << 3; template<int> struct empty {}; // 带模板参数的空结构体 template<std::uint8_t flags> using flag_a_type = std::conditional_t<(flags & FLAG_BIT_A), A, empty<0>>; template<std::uint8_t flags> using flag_b_type = std::conditional_t<(flags & FLAG_BIT_B), B, empty<1>>; template<std::uint8_t flags> using flag_c_type = std::conditional_t<(flags & FLAG_BIT_C), C, empty<2>>; template<std::uint8_t flags> using flag_d_type = std::conditional_t<(flags & FLAG_BIT_D), D, empty<3>>; template<std::uint8_t flags> struct build : flag_a_type<flags>, flag_b_type<flags>, flag_c_type<flags>, flag_d_type<flags> { }; int main() { build<FLAG_BIT_A | FLAG_BIT_C> foo; foo.a(); foo.c(); }
每个未激活标志对应的empty是不同的特化版本,编译器不会判定为重复直接基类。
解决方案2:递归链式继承
通过递归逐个处理标志位,用链式继承替代多重继承,避免同时继承多个empty:
#include <type_traits> #include <cstdint> struct A { void a() {} }; struct B { void b() {} }; struct C { void c() {} }; struct D { void d() {} }; constexpr std::uint8_t FLAG_BIT_A = 0b1 << 0; constexpr std::uint8_t FLAG_BIT_B = 0b1 << 1; constexpr std::uint8_t FLAG_BIT_C = 0b1 << 2; constexpr std::uint8_t FLAG_BIT_D = 0b1 << 3; struct empty {}; // 递归构建继承链 template<std::uint8_t flags, int bit = 0> struct build_impl : std::conditional_t< (flags & (0b1 << bit)), std::conditional_t<bit == 0, A, std::conditional_t<bit == 1, B, std::conditional_t<bit == 2, C, D>>>, empty>, build_impl<flags, bit + 1> {}; // 终止递归的特化 template<std::uint8_t flags> struct build_impl<flags, 4> : empty {}; template<std::uint8_t flags> using build = build_impl<flags>; int main() { build<FLAG_BIT_A | FLAG_BIT_C> foo; foo.a(); foo.c(); }
链式继承中即使出现多个empty,也是间接继承,不会触发重复直接基类的错误。
解决方案3:C++17折叠表达式+元组展开
利用C++17的折叠表达式,只继承实际激活的目标类型,完全规避empty的重复问题:
#include <type_traits> #include <cstdint> #include <tuple> struct A { void a() {} }; struct B { void b() {} }; struct C { void c() {} }; struct D { void d() {} }; constexpr std::uint8_t FLAG_BIT_A = 0b1 << 0; constexpr std::uint8_t FLAG_BIT_B = 0b1 << 1; constexpr std::uint8_t FLAG_BIT_C = 0b1 << 2; constexpr std::uint8_t FLAG_BIT_D = 0b1 << 3; // 辅助类,展开元组类型实现继承 template<typename... Ts> struct inherit_from : Ts... {}; // 特化跳过void类型 template<typename... Ts> struct inherit_from<void, Ts...> : inherit_from<Ts...> {}; template<> struct inherit_from<void> {}; template<std::uint8_t flags> struct build : inherit_from< std::conditional_t<(flags & FLAG_BIT_A), A, std::type_identity_t<void>>, std::conditional_t<(flags & FLAG_BIT_B), B, std::type_identity_t<void>>, std::conditional_t<(flags & FLAG_BIT_C), C, std::type_identity_t<void>>, std::conditional_t<(flags & FLAG_BIT_D), D, std::type_identity_t<void>> > {}; int main() { build<FLAG_BIT_A | FLAG_BIT_C> foo; foo.a(); foo.c(); }
用void作为未激活标志的占位符,通过inherit_from的特化跳过void,最终build只继承需要的目标类型。
内容的提问来源于stack exchange,提问作者Stack Danny
相关产品推荐
相关产品推荐

