Сообщение Re: placement new от 10.12.2022 22:42
Изменено 10.12.2022 22:51 Sm0ke
Re: placement new
Здравствуйте, maks1180, Вы писали:
Я бы предложил сделать обёртку, которая хранит экземпляр для смены. Назовём его keep
https://godbolt.org/z/YEcrvMTn6
Тут keep<some_base, some_derived1, some_derived2> Хранит указатель на базовый класс в поле m_base , Ведь нет гарантий, что адреса при разных emplace<some_derived_xxx>() будут одинаковыми. Например при множественном наследовании от других классов. Кроме того при (m_base == nullptr) мы точно знаем, что объект keep пуст.
Если надо в keep создать сперва базовый класс, а потом пересоздать один из наследников, но свойства базового сохранить, то я думаю стоит эти свойства объединить в отдельную структуру. Чтобы можно было их засэйвить в локальную переменную и потом пробросить в конструктор наследника через emplace<xd>(v_params).
Я бы предложил сделать обёртку, которая хранит экземпляр для смены. Назовём его keep
https://godbolt.org/z/YEcrvMTn6
#include <concepts>
#include <type_traits>
#include <algorithm>
template <typename T, typename ... U>
concept c_any_of = ( std::same_as<T, U> || ... );
template <typename T_base, std::derived_from<T_base> ... T_sub>
struct keep {
static constexpr std::size_t
s_max_size = std::max<std::size_t>({sizeof(T_base), sizeof(T_sub) ...});
static constexpr std::size_t
s_max_align = std::max<std::size_t>({alignof(T_base), alignof(T_sub) ...});
static constexpr bool
s_all_trivial_des = (
std::is_trivially_destructible_v<T_base> &&
(std::is_trivially_destructible_v<T_sub> && ...)
);
using t_data = std::aligned_storage_t<s_max_size, s_max_align>;
using pointer = T_base *;
private:
t_data m_data;
public:
pointer m_base = nullptr;
~keep() requires(! s_all_trivial_des) {
clear();
}
template <c_any_of<T_base, T_sub ...> T, typename ... T_args>
T * emplace(T_args && ... p_args) {
clear();
T * ret = new (&m_data) T(std::forward<T_args>(p_args) ...);
m_base = ret;
return ret;
}
operator bool () const {
return m_base != nullptr;
}
pointer operator -> () {
return m_base;
}
void clear() {
if( m_base != nullptr ) { m_base->~T_base(); m_base = nullptr; }
}
};
Тут keep<some_base, some_derived1, some_derived2> Хранит указатель на базовый класс в поле m_base , Ведь нет гарантий, что адреса при разных emplace<some_derived_xxx>() будут одинаковыми. Например при множественном наследовании от других классов. Кроме того при (m_base == nullptr) мы точно знаем, что объект keep пуст.
Если надо в keep создать сперва базовый класс, а потом пересоздать один из наследников, но свойства базового сохранить, то я думаю стоит эти свойства объединить в отдельную структуру. Чтобы можно было их засэйвить в локальную переменную и потом пробросить в конструктор наследника через emplace<xd>(v_params).
Re: placement new
Здравствуйте, maks1180, Вы писали:
Я бы предложил сделать обёртку, которая хранит экземпляр для смены. Назовём его keep
https://godbolt.org/z/YEcrvMTn6
Тут keep<some_base, some_derived1, some_derived2> Хранит указатель на базовый класс в поле m_base , Ведь нет гарантий, что адреса при разных emplace<some_derived_xxx>() будут одинаковыми. Например при множественном наследовании от других классов. Кроме того при (m_base == nullptr) мы точно знаем, что объект keep пуст.
Если надо в keep создать сперва базовый класс, а потом пересоздать один из наследников, но свойства базового сохранить, то я думаю стоит эти свойства объединить в отдельную структуру. Чтобы можно было их засэйвить (скопировать или мувнуть) в локальную переменную и потом пробросить в конструктор наследника через emplace<xd>(v_params).
Я бы предложил сделать обёртку, которая хранит экземпляр для смены. Назовём его keep
https://godbolt.org/z/YEcrvMTn6
#include <concepts>
#include <type_traits>
#include <algorithm>
template <typename T, typename ... U>
concept c_any_of = ( std::same_as<T, U> || ... );
template <typename T_base, std::derived_from<T_base> ... T_sub>
struct keep {
static constexpr std::size_t
s_max_size = std::max<std::size_t>({sizeof(T_base), sizeof(T_sub) ...});
static constexpr std::size_t
s_max_align = std::max<std::size_t>({alignof(T_base), alignof(T_sub) ...});
static constexpr bool
s_all_trivial_des = (
std::is_trivially_destructible_v<T_base> &&
(std::is_trivially_destructible_v<T_sub> && ...)
);
using t_data = std::aligned_storage_t<s_max_size, s_max_align>;
using pointer = T_base *;
private:
t_data m_data;
public:
pointer m_base = nullptr;
~keep() requires(! s_all_trivial_des) {
clear();
}
template <c_any_of<T_base, T_sub ...> T, typename ... T_args>
T * emplace(T_args && ... p_args) {
clear();
T * ret = new (&m_data) T(std::forward<T_args>(p_args) ...);
m_base = ret;
return ret;
}
operator bool () const {
return m_base != nullptr;
}
pointer operator -> () {
return m_base;
}
void clear() {
if( m_base != nullptr ) { m_base->~T_base(); m_base = nullptr; }
}
};
Тут keep<some_base, some_derived1, some_derived2> Хранит указатель на базовый класс в поле m_base , Ведь нет гарантий, что адреса при разных emplace<some_derived_xxx>() будут одинаковыми. Например при множественном наследовании от других классов. Кроме того при (m_base == nullptr) мы точно знаем, что объект keep пуст.
Если надо в keep создать сперва базовый класс, а потом пересоздать один из наследников, но свойства базового сохранить, то я думаю стоит эти свойства объединить в отдельную структуру. Чтобы можно было их засэйвить (скопировать или мувнуть) в локальную переменную и потом пробросить в конструктор наследника через emplace<xd>(v_params).