From a220880dc12ff40b59130e9da0f8dfb02bef96e2 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Fri, 4 Oct 2024 21:36:51 +0200 Subject: add compressed pair example --- compressed_pair.cc | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 compressed_pair.cc (limited to 'compressed_pair.cc') diff --git a/compressed_pair.cc b/compressed_pair.cc new file mode 100644 index 0000000..873c4ce --- /dev/null +++ b/compressed_pair.cc @@ -0,0 +1,101 @@ +#include +#if 0 +set -xe +g++ -std=c++17 -fsyntax-only compressed_pair.cc +clang++ -std=c++17 -fsyntax-only compressed_pair.cc +g++ -std=c++20 -fsyntax-only compressed_pair.cc +clang++ -std=c++20 -fsyntax-only compressed_pair.cc +exit 0 +#endif + +#include + +// Minimal compressed pair implementation. +// +// In cpp even if a class is zero-sized, adding a member of such a class, +// allocates 1 byte in the layout of the instantiating class. In generic +// programming one sometimes wants to store a member of a template argument and +// in the best case for zero-sized template types one does not want to pay a +// price. +// +// One example for this is std::unique_ptr with a custom state-less deleter: +// +#include +struct freer { + void operator()(void* p) { + std::free(p); + } +}; + +std::unique_ptr ptr{nullptr}; +static_assert(sizeof(ptr) == sizeof(char*), ""); +// +// Before cpp20 this can be achieved with some template specialization tricks +// and deriving from the type if it is not empty and not final. +// +// Starting with cpp20 the [[no_unique_address]] attribute [1] was added which +// allows the compiler to not allocate space in the layout of a class for a +// given member if that is zero-sized. +// +// [1] https://en.cppreference.com/w/cpp/language/attributes/no_unique_address + +namespace cpp11 { +template && !std::is_final_v> +struct entry { + T& get() { + return val; + } + + T val; +}; + +template +struct entry : T { + T& get() { + return *static_cast(this); + } +}; + +template +struct pair : entry, entry { + T& first() { + return static_cast*>(this)->get(); + } + + U& second() { + return static_cast*>(this)->get(); + } +}; +} // namespace cpp11 + +struct empty {}; + +#if __cplusplus >= 202002L +namespace cpp20 { +template +struct pair { + T& first() { + return m_first; + } + + U& second() { + return m_second; + } + + [[no_unique_address]] T m_first; + [[no_unique_address]] U m_second; +}; +} // namespace cpp20 +#endif + +// -- TEST ME ------------------------------------------------------------------ + +static_assert(sizeof(cpp11::pair) == 8); +static_assert(sizeof(cpp11::pair) == 4); +static_assert(sizeof(cpp11::pair) == 2); + +#if __cplusplus >= 202002L +static_assert(sizeof(cpp20::pair) == 8); +static_assert(sizeof(cpp20::pair) == 4); +static_assert(sizeof(cpp20::pair) == 2); +#endif -- cgit v1.2.3