Информация об изменениях

Сообщение Re: placement new от 10.12.2022 22:42

Изменено 10.12.2022 22:51 Sm0ke

Re: placement new
Здравствуйте, maks1180, Вы писали:

Я бы предложил сделать обёртку, которая хранит экземпляр для смены. Назовём его 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
#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).