Насколько я могу судить, это невозможно, если вы хотите иметь обоих участников. Но вы можете специализироваться и иметь только один из членов, когда тип такой же и пустой:
template <typename T, typename S, typename = void>
struct Empty{
[[no_unique_address]] T t;
[[no_unique_address]] S s;
constexpr T& get_t() noexcept { return t; };
constexpr S& get_s() noexcept { return s; };
};
template<typename TS>
struct Empty<TS, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
[[no_unique_address]] TS ts;
constexpr TS& get_t() noexcept { return ts; };
constexpr TS& get_s() noexcept { return ts; };
};
Конечно, остальная часть программы, которая использует участников, должна быть изменена, чтобы иметь дело со случаем, когда есть только один участник. Не имеет значения, какой элемент используется в этом случае - в конце концов, это объект без состояния без уникального адреса. Показанные функции-члены должны сделать это простым.
к сожалению, sizeof(Empty<Empty<A,A>,A>{})==2
где A является совершенно пустой структурой.
Вы можете ввести больше специализаций для поддержки рекурсивного сжатия пустых пар:
template<class TS>
struct Empty<Empty<TS, TS>, TS, typename std::enable_if_t<std::is_empty_v<TS>>>{
[[no_unique_address]] Empty<TS, TS> ts;
constexpr Empty<TS, TS>& get_t() noexcept { return ts; };
constexpr TS& get_s() noexcept { return ts.get_s(); };
};
template<class TS>
struct Empty<TS, Empty<TS, TS>, typename std::enable_if_t<std::is_empty_v<TS>>>{
[[no_unique_address]] Empty<TS, TS> ts;
constexpr TS& get_t() noexcept { return ts.get_t(); };
constexpr Empty<TS, TS>& get_s() noexcept { return ts; };
};
Еще больше, чтобы сжать что-то вроде Empty<Empty<A, char>, A>
.
template <typename T, typename S>
struct Empty<Empty<T, S>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
[[no_unique_address]] Empty<T, S> ts;
constexpr Empty<T, S>& get_t() noexcept { return ts; };
constexpr S& get_s() noexcept { return ts.get_s(); };
};
template <typename T, typename S>
struct Empty<Empty<S, T>, S, typename std::enable_if_t<std::is_empty_v<S>>>{
[[no_unique_address]] Empty<S, T> st;
constexpr Empty<S, T>& get_t() noexcept { return st; };
constexpr S& get_s() noexcept { return st.get_t(); };
};
template <typename T, typename S>
struct Empty<T, Empty<T, S>, typename std::enable_if_t<std::is_empty_v<T>>>{
[[no_unique_address]] Empty<T, S> ts;
constexpr T& get_t() noexcept { return ts.get_t(); };
constexpr Empty<T, S> get_s() noexcept { return ts; };
};
template <typename T, typename S>
struct Empty<T, Empty<S, T>, typename std::enable_if_t<std::is_empty_v<T>>>{
[[no_unique_address]] Empty<S, T> st;
constexpr T& get_t() noexcept { return st.get_s(); };
constexpr Empty<S, T> get_s() noexcept { return st; };
};
T
себе? Это будет генерировать различные типы. Прямо сейчас тот факт, что обаWrapper
унаследовали отT
вас, сдерживает вас ...