diff --git a/include/seastar/core/future.hh b/include/seastar/core/future.hh index cc5c91220a..d24e7d48e9 100644 --- a/include/seastar/core/future.hh +++ b/include/seastar/core/future.hh @@ -286,24 +286,19 @@ using maybe_wrap_ref = std::conditional_t, std::reference /// /// This is similar to a std::optional, but it doesn't know if it is holding a value or not, so the user is /// responsible for calling constructors and destructors. -/// -/// The advantage over just using a union directly is that this uses inheritance when possible and so benefits from the -/// empty base optimization. -template -struct uninitialized_wrapper_base; template -struct uninitialized_wrapper_base { +struct uninitialized_wrapper { using tuple_type = future_tuple_type_t; - union any { + [[no_unique_address]] union any { any() noexcept {} ~any() {} // T can be a reference, so wrap it. - maybe_wrap_ref value; + [[no_unique_address]] maybe_wrap_ref value; } _v; public: - uninitialized_wrapper_base() noexcept = default; + uninitialized_wrapper() noexcept = default; template std::enable_if_t...>, std::tuple>, void> uninitialized_set(U&&... vs) { @@ -323,43 +318,27 @@ public: } }; -template struct uninitialized_wrapper_base : private T { - using tuple_type = future_tuple_type_t; - uninitialized_wrapper_base() noexcept = default; - template - std::enable_if_t...>, std::tuple>, void> - uninitialized_set(U&&... vs) { - new (this) T(std::forward(vs)...); +template <> +struct uninitialized_wrapper { + [[no_unique_address]] internal::monostate _v; +public: + uninitialized_wrapper() noexcept = default; + void uninitialized_set() { } - void uninitialized_set(tuple_type&& v) { - if constexpr (std::tuple_size_v != 0) { - uninitialized_set(std::move(std::get<0>(v))); - } + void uninitialized_set(internal::monostate) { } - void uninitialized_set(const tuple_type& v) { - if constexpr (std::tuple_size_v != 0) { - uninitialized_set(std::get<0>(v)); - } + void uninitialized_set(std::tuple<>&& v) { } - T& uninitialized_get() { - return *this; + void uninitialized_set(const std::tuple<>& v) { } - const T& uninitialized_get() const { - return *this; + internal::monostate& uninitialized_get() { + return _v; + } + const internal::monostate& uninitialized_get() const { + return _v; } }; -template -constexpr bool can_inherit = - (std::is_trivially_destructible_v && std::is_trivially_constructible_v && - std::is_class_v && !std::is_final_v); - -// The objective is to avoid extra space for empty types like std::tuple<>. We could use std::is_empty_v, but it is -// better to check that both the constructor and destructor can be skipped. -template -struct uninitialized_wrapper - : public uninitialized_wrapper_base> {}; - template struct is_trivially_move_constructible_and_destructible { static constexpr bool value = std::is_trivially_move_constructible_v && std::is_trivially_destructible_v;