Empty type optimizations

C++20 introduced [[no_unique_address]] attribute, which makes empty class fields not waste any memory. In generic code it’s a good practice to apply it to all the class fields, unless a field is known to be non-empty. Previously, the only way to achieve a similar result was to use empty base optimization and compressed pairs based on it, which is very inconvenient and doesn’t work for final classes.

Unfortunately, the standard attribute is ignored in the current MSVC version. For portability we need a custom wrapper until that’s fixed. See MSVC blog, github issue. Moreover, MSVC has issues with multiple inheritance support, requiring even move workarounds: MSVC blog.

Reference

#include <actl/memory/empty_type/EmptyTrivial.hpp>
template<typename T>
concept EmptyTrivial

Concept of an empty type with an additional requirement to be trivial.

Objects of such type can be efficiently created on the fly instead of adding [[no_unique_address]] complexity for efficient storage. Even with [[no_unique_address]], storage can use extra memory in case the same empty type is used multiple times.

Having all of default/copy/move constructors and destructor to be trivial, that is no-op for an empty type, implies that creating an object on the fly instead of storing it has the same observable behavior.

This requirement holds for the typical use cases for empty types, such as function objects.

See tests at tests/memory/empty_type/EmptyTrivial.cpp

#include <actl/memory/empty_type/AC_NO_UNIQUE_ADDRESS.hpp>
AC_NO_UNIQUE_ADDRESS

Portable replacement for [[no_unique_address]] attribute.

See tests at tests/memory/empty_type/AC_NO_UNIQUE_ADDRESS.cpp

#include <actl/memory/empty_type/AC_EMPTY_BASES.hpp>
AC_EMPTY_BASES

Workaround to make EBO work as expected on MSVC with multiple inheritance:

struct AC_EMPTY_BASES derived : empty1, empty2 {
    int i;
};
static_assert(sizeof(derived) == sizeof(int));

See tests at tests/memory/empty_type/AC_EMPTY_BASES.cpp