How to emulate EBO when using raw storage?

I think you gave the answer yourself in your various observations:

  1. You want raw memory and placement new. This requires to have at least one byte available, even if you want to construct an empty object via placement new.
  2. You want zero bytes overhead for storing any empty objects.

These requirements are self-contradicting. The answer therefore is No, that is not possible.

You could change your requirements a bit more, though, by requiring the zero byte overhead only for empty, trivial types.

You could define a new class trait, e.g.

template <typename T>
struct constructor_and_destructor_are_empty : std::false_type
{
};

Then you specialize

template <typename T, typename = void>
class raw_container;

template <typename T>
class raw_container<
    T,
    std::enable_if_t<
        std::is_empty<T>::value and
        std::is_trivial<T>::value>>
{
public:
  T& data() noexcept
  {
    return reinterpret_cast<T&>(*this);
  }
  void construct()
  {
    // do nothing
  }
  void destruct()
  {
    // do nothing
  }
};

template <typename T>
struct list_node : public raw_container<T>
{
  std::atomic<list_node*> next_;
};

Then use it like this:

using node = list_node<empty<char>>;
static_assert(sizeof(node) == sizeof(std::atomic<node*>), "Good");

Of course, you still have

struct bar : raw_container<empty<char>> { empty<char> e; };
static_assert(sizeof(bar) == 1, "Yes, two objects sharing an address");

But that is normal for EBO:

struct ebo1 : empty<char>, empty<usigned char> {};
static_assert(sizeof(ebo1) == 1, "Two object in one place");
struct ebo2 : empty<char> { char c; };
static_assert(sizeof(ebo2) == 1, "Two object in one place");

But as long as you always use construct and destruct and no placement new on &data(), you’re golden.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)