Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how can I achieve multiple conditional inheritance?

I have a type build that has a flag template, and according to the active flag bits, it inherits from those types. this allows me to "build" classes from many subclasses with a great number of configurations:

#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;
}

so build<FLAG_BIT_A | FLAG_BIT_C> should result in a class that inherits from A and from C.

but it doesn't compile, saying empty is already a direct base class:

error C2500: 'build<5>': 'empty' is already a direct base class

how can I achieve this without having to make 4 different empty structs to avoid the clash?

like image 419
Stack Danny Avatar asked Sep 10 '25 22:09

Stack Danny


1 Answers

Here's another approach using c++20 that provides much more flexibility for bitmask-based inheritance:

#include <concepts>
#include <cstdint>

template <std::integral auto, class...>
struct inherit_mask {};

template <auto flags, class Base, class... Bases>
  requires((flags & 1) == 1)
struct inherit_mask<flags, Base, Bases...>
    : Base, inherit_mask<(flags >> 1), Bases...> {};

template <auto flags, class Base, class... Bases>
struct inherit_mask<flags, Base, Bases...>
    : inherit_mask<(flags >> 1), Bases...> {};

struct A { void a() {} };
struct B { void b() {} };
struct C { void c() {} };
struct D { void d() {} };

template <std::uint8_t flags>
using build = inherit_mask<flags, A, B, C, D>;

using foo = build<0b0101>;

static_assert(std::derived_from<foo, A>);
static_assert(not std::derived_from<foo, B>);
static_assert(std::derived_from<foo, C>);
static_assert(not std::derived_from<foo, D>);

Compiler Explorer

Works on clang, gcc, and msvc, and doesn't lead to exponential instantiation explosion.

like image 122
Patrick Roberts Avatar answered Sep 13 '25 15:09

Patrick Roberts