fork download
  1. #include <cassert>
  2. #include <new>
  3. #include <utility>
  4. #include <tuple>
  5. #include <typeinfo>
  6.  
  7. class holder_base {
  8. public:
  9. virtual void holder_copy_construct(void* dst) const { assert(false); };
  10. virtual void holder_move_construct(void* dst) noexcept { assert(false); };
  11. virtual void holder_copy_assign(holder_base& dst) const { assert(false); };
  12. virtual void holder_move_assign(holder_base& dst) noexcept { assert(false); };
  13. virtual void holder_swap(holder_base& other) noexcept { assert(false); };
  14. virtual ~holder_base() noexcept {};
  15. };
  16.  
  17. template <typename T, typename U, std::enable_if_t<
  18. std::is_convertible<int std::decay_t<U>::*, int std::decay_t<T>::*>{}
  19. || std::is_convertible<std::remove_reference_t<U>*, std::remove_reference_t<T>*>{}, int> = 0>
  20. T fast_cast(U&& v) noexcept { return static_cast<T>(std::forward<U>(v)); }
  21. template <typename T, typename U, std::enable_if_t<
  22. !std::is_convertible<int std::decay_t<U>::*, int std::decay_t<T>::*>{}
  23. && !std::is_convertible<std::remove_reference_t<U>*, std::remove_reference_t<T>*>{}, int> = 0>
  24. T fast_cast(U&& v) noexcept { return dynamic_cast<T>(std::forward<U>(v)); }
  25.  
  26. template <typename T, bool B = std::is_base_of<holder_base, T>{}>
  27. struct holder_helper : T {
  28. template <typename U>
  29. holder_helper(U&& v) : T{std::forward<U>(v)} {}
  30. };
  31. template <typename T>
  32. struct holder_helper<T, false> : holder_base, T {
  33. template <typename U>
  34. holder_helper(U&& v) : holder_base{}, T{std::forward<U>(v)} {}
  35. };
  36.  
  37. template <typename T>
  38. class holder : public holder_helper<T> {
  39. public:
  40. template <typename U>
  41. explicit holder(U&& v) noexcept(noexcept(std::decay_t<U>{std::forward<U>(v)}))
  42. : holder_helper<T>{std::forward<U>(v)}
  43. {}
  44. private:
  45. virtual void holder_copy_construct(void* dst) const final override {
  46. new(dst) holder{static_cast<const T&>(*this)};
  47. }
  48. virtual void holder_move_construct(void* dst) noexcept final override {
  49. new(dst) holder{static_cast<T&&>(*this)};
  50. }
  51. template <typename U, std::enable_if_t<std::is_nothrow_copy_constructible<U>{}, int> = 0>
  52. static void holder_copy_assign(holder_base& dst, const U& src) noexcept {
  53. dst.~holder_base();
  54. src.holder_copy_construct(&dst);
  55. }
  56. template <typename U, std::enable_if_t<!std::is_nothrow_copy_constructible<U>{}, int> = 0>
  57. static void holder_copy_assign(holder_base& dst, const U& src) {
  58. T tmp{static_cast<const T&>(src)};
  59. dst.~holder_base();
  60. new(static_cast<void*>(&dst)) holder{std::move(tmp)};
  61. }
  62. virtual void holder_copy_assign(holder_base& dst) const final override {
  63. if (typeid(dst) == typeid(holder))
  64. static_cast<T&>(fast_cast<holder&>(dst)) = static_cast<const T&>(*this);
  65. else
  66. holder_copy_assign(dst, *this);
  67. }
  68. virtual void holder_move_assign(holder_base& dst) noexcept final override {
  69. if (typeid(dst) == typeid(holder))
  70. static_cast<T&>(fast_cast<holder&>(dst)) = static_cast<T&&>(*this);
  71. else {
  72. dst.~holder_base();
  73. holder_move_construct(&dst);
  74. }
  75. }
  76. virtual void holder_swap(holder_base& other) noexcept final override {
  77. if (typeid(other) == typeid(holder)) {
  78. using std::swap;
  79. swap(static_cast<T&>(fast_cast<holder&>(other)), static_cast<T&>(*this));
  80. } else {
  81. T tmp{static_cast<T&&>(*this)};
  82. this->~holder();
  83. other.holder_move_construct(this);
  84. other.~holder_base();
  85. new(static_cast<void*>(&other)) holder{std::move(tmp)};
  86. }
  87. }
  88. };
  89.  
  90. template <typename T, std::size_t max_size = sizeof(holder<T>), std::size_t alignment = alignof(holder<T>)>
  91. class polymorph {
  92. static_assert(std::is_polymorphic<T>{}, "Base must be polymorphic");
  93. static_assert(std::has_virtual_destructor<T>{}, "Base must have virtual destructor");
  94. static_assert(max_size >= sizeof(holder<T>), "specified size is unsufficient");
  95. static_assert(alignment >= alignof(holder<T>), "specified alignment not strict enough");
  96. static_assert(max_size % alignment == 0, "specified size and alignment inconsistent");
  97. public:
  98. template <typename U>
  99. polymorph(U&& src) noexcept(noexcept(std::decay_t<U>{std::forward<U>(src)})) {
  100. using src_type = std::decay_t<U>;
  101. static_assert(std::is_convertible<src_type*, T*>{}, "Base must be unambigous public base of Argument");
  102. assert(typeid(src_type) == typeid(src)); // argument needs to be of the most derived type
  103. static_assert(!std::is_final<src_type>{}, "argument type must not be final");
  104. static_assert(sizeof(holder<src_type>) <= max_size, "argument size too big");
  105. static_assert(alignof(holder<src_type>) <= alignment, "argument alignment too strict");
  106. static_assert(std::is_nothrow_move_constructible<src_type>{}, "argument must be nothrow move constructible");
  107. static_assert(std::is_nothrow_move_assignable<src_type>{}, "argument must be nothrow move assignable");
  108. static_assert(std::is_nothrow_destructible<src_type>{}, "argument must be nothrow destructible");
  109. // static_assert(std::is_nothrow_swappable_v<src_type>);
  110.  
  111. auto p = new(static_cast<void*>(data)) holder<src_type>{std::forward<U>(src)};
  112. assert(static_cast<holder_base*>(p) == static_cast<void*>(p)); // sane layout
  113. }
  114. polymorph(const polymorph& other) { other.holder_get().holder_copy_construct(data); }
  115. polymorph(polymorph&& other) noexcept { other.holder_get().holder_move_construct(data); }
  116. polymorph& operator=(const polymorph& rhs) { rhs.holder_get().holder_copy_assign(holder_get()); return *this; }
  117. polymorph& operator=(polymorph&& rhs) noexcept { rhs.holder_get().holder_move_assign(holder_get()); return *this; }
  118. ~polymorph() noexcept { this->holder_get().~holder_base(); }
  119.  
  120. void swap(polymorph& other) noexcept { holder_get().holder_swap(other.holder_get()); }
  121. void swap(polymorph&& other) noexcept { swap(other); }
  122.  
  123. T& get() noexcept { return fast_cast<T&>(holder_get()); }
  124. const T& get() const noexcept { return fast_cast<const T&>(holder_get()); }
  125. private:
  126. holder_base& holder_get() noexcept { return reinterpret_cast<holder_base&>(data); }
  127. const holder_base& holder_get() const noexcept { return reinterpret_cast<const holder_base&>(data); }
  128. alignas(alignment) char data[max_size];
  129. };
  130.  
  131. template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>& x, polymorph<T, S, A>& y) noexcept { return x.swap(y); }
  132. template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>&& x, polymorph<T, S, A>& y) noexcept { return x.swap(y); }
  133. template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>& x, polymorph<T, S, A>&& y) noexcept { return x.swap(y); }
  134. template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>&& x, polymorph<T, S, A>&& y) noexcept { return x.swap(y); }
  135. ////////////////////////////////////////////////////////////////////////////////////////////
  136. #include <iostream>
  137. #include <vector>
  138. struct base : holder_base {
  139. virtual void foo() = 0;
  140. virtual ~base() {}
  141. };
  142. template <int I>
  143. struct derived : base
  144. {
  145. derived() { std::cout << (void*)this << " derived<" << I << ">::derived()\n"; }
  146. derived(const derived&) { std::cout << (void*)this << " derived<" << I << ">::derived(const derived&)\n"; }
  147. derived(derived&&) noexcept { std::cout << (void*)this << " derived<" << I << ">::derived(derived&&)\n"; }
  148. derived& operator=(const derived&) { std::cout << (void*)this << " derived<" << I << ">& derived<" << I << ">::operator=(const derived&)\n"; return *this; }
  149. derived& operator=(derived&&) noexcept { std::cout << (void*)this << " derived<" << I << ">& derived<" << I << ">::operator=(derived&&)\n"; return *this; }
  150. ~derived() noexcept { std::cout << (void*)this << " derived<" << I << ">::~derived()\n"; }
  151. virtual void foo() override { std::cout << (void*)this << " derived<" << I << ">::foo()\n"; }
  152. };
  153.  
  154. int main() {
  155. std::vector<polymorph<base>> v;
  156. std::cout << "v.emplace_back(derived<0>{});\n";
  157. v.emplace_back(derived<0>{});
  158. std::cout << "v.push_back(derived<1>{});\n";
  159. v.push_back(derived<1>{});
  160. std::cout << "v.emplace_back(derived<2>{});\n";
  161. v.emplace_back(derived<2>{});
  162. std::cout << "***\n";
  163. for ( auto& x : v )
  164. x.get().foo();
  165. std::cout << "v[0] = std::move(v[0]);\n";
  166. v[0] = std::move(v[0]);
  167. std::cout << "v[0] = std::move(v[2]);\n";
  168. v[0] = std::move(v[2]);
  169. std::cout << "v[0] = v[0];\n";
  170. v[0] = v[0];
  171. std::cout << "v[0] = v[1];\n";
  172. v[0] = v[1];
  173. std::cout << "swap(v[0],v[0]);\n";
  174. swap(v[0],v[0]);
  175. std::cout << "swap(v[0],v[2]);\n";
  176. swap(v[0],v[2]);
  177. std::cout << "***\n";
  178. for ( auto& x : v )
  179. x.get().foo();
  180. std::cout << "return 0;\n";
  181. }
Success #stdin #stdout 0s 4540KB
stdin
Standard input is empty
stdout
v.emplace_back(derived<0>{});
0x7ffe87ec2a00 derived<0>::derived()
0x55715dccec30 derived<0>::derived(derived&&)
0x7ffe87ec2a00 derived<0>::~derived()
v.push_back(derived<1>{});
0x7ffe87ec2a10 derived<1>::derived()
0x7ffe87ec2a20 derived<1>::derived(derived&&)
0x55715dccec58 derived<1>::derived(derived&&)
0x55715dccec50 derived<0>::derived(derived&&)
0x55715dccec30 derived<0>::~derived()
0x7ffe87ec2a20 derived<1>::~derived()
0x7ffe87ec2a10 derived<1>::~derived()
v.emplace_back(derived<2>{});
0x7ffe87ec2a20 derived<2>::derived()
0x55715dccec80 derived<2>::derived(derived&&)
0x55715dccec70 derived<0>::derived(derived&&)
0x55715dccec78 derived<1>::derived(derived&&)
0x55715dccec50 derived<0>::~derived()
0x55715dccec58 derived<1>::~derived()
0x7ffe87ec2a20 derived<2>::~derived()
***
0x55715dccec70 derived<0>::foo()
0x55715dccec78 derived<1>::foo()
0x55715dccec80 derived<2>::foo()
v[0] = std::move(v[0]);
0x55715dccec70 derived<0>& derived<0>::operator=(derived&&)
v[0] = std::move(v[2]);
0x55715dccec70 derived<0>::~derived()
0x55715dccec70 derived<2>::derived(derived&&)
v[0] = v[0];
0x55715dccec70 derived<2>& derived<2>::operator=(const derived&)
v[0] = v[1];
0x7ffe87ec29c0 derived<1>::derived(const derived&)
0x55715dccec70 derived<2>::~derived()
0x55715dccec70 derived<1>::derived(derived&&)
0x7ffe87ec29c0 derived<1>::~derived()
swap(v[0],v[0]);
0x7ffe87ec29c0 derived<1>::derived(derived&&)
0x55715dccec70 derived<1>& derived<1>::operator=(derived&&)
0x55715dccec70 derived<1>& derived<1>::operator=(derived&&)
0x7ffe87ec29c0 derived<1>::~derived()
swap(v[0],v[2]);
0x7ffe87ec29c0 derived<1>::derived(derived&&)
0x55715dccec70 derived<1>::~derived()
0x55715dccec70 derived<2>::derived(derived&&)
0x55715dccec80 derived<2>::~derived()
0x55715dccec80 derived<1>::derived(derived&&)
0x7ffe87ec29c0 derived<1>::~derived()
***
0x55715dccec70 derived<2>::foo()
0x55715dccec78 derived<1>::foo()
0x55715dccec80 derived<1>::foo()
return 0;
0x55715dccec70 derived<2>::~derived()
0x55715dccec78 derived<1>::~derived()
0x55715dccec80 derived<1>::~derived()