value_storage.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /* Essentially an internal optional implementation :)
  2. (C) 2017-2019 Niall Douglas <http://www.nedproductions.biz/> (24 commits)
  3. File Created: June 2017
  4. Boost Software License - Version 1.0 - August 17th, 2003
  5. Permission is hereby granted, free of charge, to any person or organization
  6. obtaining a copy of the software and accompanying documentation covered by
  7. this license (the "Software") to use, reproduce, display, distribute,
  8. execute, and transmit the Software, and to prepare derivative works of the
  9. Software, and to permit third-parties to whom the Software is furnished to
  10. do so, all subject to the following:
  11. The copyright notices in the Software and this entire statement, including
  12. the above license grant, this restriction and the following disclaimer,
  13. must be included in all copies of the Software, in whole or in part, and
  14. all derivative works of the Software, unless such copies or derivative
  15. works are solely in the form of machine-executable object code generated by
  16. a source language processor.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  20. SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  21. FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  22. ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. DEALINGS IN THE SOFTWARE.
  24. */
  25. #ifndef BOOST_OUTCOME_VALUE_STORAGE_HPP
  26. #define BOOST_OUTCOME_VALUE_STORAGE_HPP
  27. #include "../config.hpp"
  28. BOOST_OUTCOME_V2_NAMESPACE_BEGIN
  29. namespace detail
  30. {
  31. template <class T, bool nothrow> struct strong_swap_impl
  32. {
  33. constexpr strong_swap_impl(bool &allgood, T &a, T &b)
  34. {
  35. allgood = true;
  36. using std::swap;
  37. swap(a, b);
  38. }
  39. };
  40. #ifndef BOOST_NO_EXCEPTIONS
  41. template <class T> struct strong_swap_impl<T, false>
  42. {
  43. strong_swap_impl(bool &allgood, T &a, T &b)
  44. {
  45. allgood = true;
  46. T v(static_cast<T &&>(a));
  47. try
  48. {
  49. a = static_cast<T &&>(b);
  50. }
  51. catch(...)
  52. {
  53. // Try to put back a
  54. try
  55. {
  56. a = static_cast<T &&>(v);
  57. // fall through as all good
  58. }
  59. catch(...)
  60. {
  61. // failed to completely restore
  62. allgood = false;
  63. // throw away second exception
  64. }
  65. throw; // rethrow original exception
  66. }
  67. // b has been moved to a, try to move v to b
  68. try
  69. {
  70. b = static_cast<T &&>(v);
  71. }
  72. catch(...)
  73. {
  74. // Try to restore a to b, and v to a
  75. try
  76. {
  77. b = static_cast<T &&>(a);
  78. a = static_cast<T &&>(v);
  79. // fall through as all good
  80. }
  81. catch(...)
  82. {
  83. // failed to completely restore
  84. allgood = false;
  85. // throw away second exception
  86. }
  87. throw; // rethrow original exception
  88. }
  89. }
  90. };
  91. #endif
  92. } // namespace detail
  93. /*!
  94. */
  95. BOOST_OUTCOME_TEMPLATE(class T)
  96. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(std::is_move_constructible<T>::value &&std::is_move_assignable<T>::value))
  97. constexpr inline void strong_swap(bool &allgood, T &a, T &b) noexcept(detail::is_nothrow_swappable<T>::value)
  98. {
  99. detail::strong_swap_impl<T, detail::is_nothrow_swappable<T>::value>(allgood, a, b);
  100. }
  101. namespace detail
  102. {
  103. using status_bitfield_type = uint32_t;
  104. // WARNING: These bits are not tracked by abi-dumper, but changing them will break ABI!
  105. static constexpr status_bitfield_type status_have_value = (1U << 0U);
  106. static constexpr status_bitfield_type status_have_error = (1U << 1U);
  107. static constexpr status_bitfield_type status_have_exception = (1U << 2U);
  108. static constexpr status_bitfield_type status_lost_consistency = (1U << 3U); // failed to complete a strong swap
  109. static constexpr status_bitfield_type status_error_is_errno = (1U << 4U); // can errno be set from this error?
  110. // bit 7 unused
  111. // bits 8-15 unused
  112. // bits 16-31 used for user supplied 16 bit value
  113. static constexpr status_bitfield_type status_2byte_shift = 16;
  114. static constexpr status_bitfield_type status_2byte_mask = (0xffffU << status_2byte_shift);
  115. // Used if T is trivial
  116. template <class T> struct value_storage_trivial
  117. {
  118. using value_type = T;
  119. union {
  120. empty_type _empty;
  121. devoid<T> _value;
  122. };
  123. status_bitfield_type _status{0};
  124. constexpr value_storage_trivial() noexcept
  125. : _empty{}
  126. {
  127. }
  128. // Special from-void catchall constructor, always constructs default T irrespective of whether void is valued or not (can do no better if T cannot be copied)
  129. struct disable_void_catchall
  130. {
  131. };
  132. using void_value_storage_trivial = std::conditional_t<std::is_void<T>::value, disable_void_catchall, value_storage_trivial<void>>;
  133. explicit constexpr value_storage_trivial(const void_value_storage_trivial &o) noexcept(std::is_nothrow_default_constructible<value_type>::value)
  134. : _value()
  135. , _status(o._status)
  136. {
  137. }
  138. value_storage_trivial(const value_storage_trivial &) = default; // NOLINT
  139. value_storage_trivial(value_storage_trivial &&) = default; // NOLINT
  140. value_storage_trivial &operator=(const value_storage_trivial &) = default; // NOLINT
  141. value_storage_trivial &operator=(value_storage_trivial &&) = default; // NOLINT
  142. ~value_storage_trivial() = default;
  143. constexpr explicit value_storage_trivial(status_bitfield_type status)
  144. : _empty()
  145. , _status(status)
  146. {
  147. }
  148. template <class... Args>
  149. constexpr explicit value_storage_trivial(in_place_type_t<value_type> /*unused*/, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args...>::value)
  150. : _value(static_cast<Args &&>(args)...)
  151. , _status(status_have_value)
  152. {
  153. }
  154. template <class U, class... Args>
  155. constexpr value_storage_trivial(in_place_type_t<value_type> /*unused*/, std::initializer_list<U> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<U>, Args...>::value)
  156. : _value(il, static_cast<Args &&>(args)...)
  157. , _status(status_have_value)
  158. {
  159. }
  160. template <class U> static constexpr bool enable_converting_constructor = !std::is_same<std::decay_t<U>, value_type>::value && std::is_constructible<value_type, U>::value;
  161. BOOST_OUTCOME_TEMPLATE(class U)
  162. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
  163. constexpr explicit value_storage_trivial(const value_storage_trivial<U> &o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
  164. : value_storage_trivial(((o._status & status_have_value) != 0) ? value_storage_trivial(in_place_type<value_type>, o._value) : value_storage_trivial()) // NOLINT
  165. {
  166. _status = o._status;
  167. }
  168. BOOST_OUTCOME_TEMPLATE(class U)
  169. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
  170. constexpr explicit value_storage_trivial(value_storage_trivial<U> &&o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
  171. : value_storage_trivial(((o._status & status_have_value) != 0) ? value_storage_trivial(in_place_type<value_type>, static_cast<U &&>(o._value)) : value_storage_trivial()) // NOLINT
  172. {
  173. _status = o._status;
  174. }
  175. constexpr void swap(value_storage_trivial &o) noexcept
  176. {
  177. // storage is trivial, so just use assignment
  178. auto temp = static_cast<value_storage_trivial &&>(*this);
  179. *this = static_cast<value_storage_trivial &&>(o);
  180. o = static_cast<value_storage_trivial &&>(temp);
  181. }
  182. };
  183. // Used if T is non-trivial
  184. template <class T> struct value_storage_nontrivial
  185. {
  186. using value_type = T;
  187. union {
  188. empty_type _empty;
  189. value_type _value;
  190. };
  191. status_bitfield_type _status{0};
  192. value_storage_nontrivial() noexcept
  193. : _empty{}
  194. {
  195. }
  196. value_storage_nontrivial &operator=(const value_storage_nontrivial &) = default; // if reaches here, copy assignment is trivial
  197. value_storage_nontrivial &operator=(value_storage_nontrivial &&) = default; // NOLINT if reaches here, move assignment is trivial
  198. value_storage_nontrivial(value_storage_nontrivial &&o) noexcept(std::is_nothrow_move_constructible<value_type>::value) // NOLINT
  199. : _status(o._status)
  200. {
  201. if(this->_status & status_have_value)
  202. {
  203. this->_status &= ~status_have_value;
  204. new(&_value) value_type(static_cast<value_type &&>(o._value)); // NOLINT
  205. _status = o._status;
  206. }
  207. }
  208. value_storage_nontrivial(const value_storage_nontrivial &o) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
  209. : _status(o._status)
  210. {
  211. if(this->_status & status_have_value)
  212. {
  213. this->_status &= ~status_have_value;
  214. new(&_value) value_type(o._value); // NOLINT
  215. _status = o._status;
  216. }
  217. }
  218. // Special from-void constructor, constructs default T if void valued
  219. explicit value_storage_nontrivial(const value_storage_trivial<void> &o) noexcept(std::is_nothrow_default_constructible<value_type>::value)
  220. : _status(o._status)
  221. {
  222. if(this->_status & status_have_value)
  223. {
  224. this->_status &= ~status_have_value;
  225. new(&_value) value_type; // NOLINT
  226. _status = o._status;
  227. }
  228. }
  229. explicit value_storage_nontrivial(status_bitfield_type status)
  230. : _empty()
  231. , _status(status)
  232. {
  233. }
  234. template <class... Args>
  235. explicit value_storage_nontrivial(in_place_type_t<value_type> /*unused*/, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args...>::value)
  236. : _value(static_cast<Args &&>(args)...) // NOLINT
  237. , _status(status_have_value)
  238. {
  239. }
  240. template <class U, class... Args>
  241. value_storage_nontrivial(in_place_type_t<value_type> /*unused*/, std::initializer_list<U> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<U>, Args...>::value)
  242. : _value(il, static_cast<Args &&>(args)...)
  243. , _status(status_have_value)
  244. {
  245. }
  246. template <class U> static constexpr bool enable_converting_constructor = !std::is_same<std::decay_t<U>, value_type>::value && std::is_constructible<value_type, U>::value;
  247. BOOST_OUTCOME_TEMPLATE(class U)
  248. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
  249. constexpr explicit value_storage_nontrivial(const value_storage_nontrivial<U> &o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
  250. : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, o._value) : value_storage_nontrivial())
  251. {
  252. _status = o._status;
  253. }
  254. BOOST_OUTCOME_TEMPLATE(class U)
  255. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
  256. constexpr explicit value_storage_nontrivial(const value_storage_trivial<U> &o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
  257. : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, o._value) : value_storage_nontrivial())
  258. {
  259. _status = o._status;
  260. }
  261. BOOST_OUTCOME_TEMPLATE(class U)
  262. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
  263. constexpr explicit value_storage_nontrivial(value_storage_nontrivial<U> &&o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
  264. : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, static_cast<U &&>(o._value)) : value_storage_nontrivial())
  265. {
  266. _status = o._status;
  267. }
  268. BOOST_OUTCOME_TEMPLATE(class U)
  269. BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
  270. constexpr explicit value_storage_nontrivial(value_storage_trivial<U> &&o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
  271. : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, static_cast<U &&>(o._value)) : value_storage_nontrivial())
  272. {
  273. _status = o._status;
  274. }
  275. ~value_storage_nontrivial() noexcept(std::is_nothrow_destructible<T>::value)
  276. {
  277. if(this->_status & status_have_value)
  278. {
  279. this->_value.~value_type(); // NOLINT
  280. this->_status &= ~status_have_value;
  281. }
  282. }
  283. constexpr void swap(value_storage_nontrivial &o) noexcept(detail::is_nothrow_swappable<value_type>::value)
  284. {
  285. using std::swap;
  286. if((_status & status_have_value) == 0 && (o._status & status_have_value) == 0)
  287. {
  288. swap(_status, o._status);
  289. return;
  290. }
  291. if((_status & status_have_value) != 0 && (o._status & status_have_value) != 0)
  292. {
  293. struct _
  294. {
  295. unsigned &a, &b;
  296. bool all_good{false};
  297. ~_()
  298. {
  299. if(!all_good)
  300. {
  301. // We lost one of the values
  302. a |= status_lost_consistency;
  303. b |= status_lost_consistency;
  304. }
  305. }
  306. } _{_status, o._status};
  307. strong_swap(_.all_good, _value, o._value);
  308. swap(_status, o._status);
  309. return;
  310. }
  311. // One must be empty and the other non-empty, so use move construction
  312. if((_status & status_have_value) != 0)
  313. {
  314. // Move construct me into other
  315. new(&o._value) value_type(static_cast<value_type &&>(_value)); // NOLINT
  316. this->_value.~value_type(); // NOLINT
  317. swap(_status, o._status);
  318. }
  319. else
  320. {
  321. // Move construct other into me
  322. new(&_value) value_type(static_cast<value_type &&>(o._value)); // NOLINT
  323. o._value.~value_type(); // NOLINT
  324. swap(_status, o._status);
  325. }
  326. }
  327. };
  328. template <class Base> struct value_storage_delete_copy_constructor : Base // NOLINT
  329. {
  330. using Base::Base;
  331. using value_type = typename Base::value_type;
  332. value_storage_delete_copy_constructor() = default;
  333. value_storage_delete_copy_constructor(const value_storage_delete_copy_constructor &) = delete;
  334. value_storage_delete_copy_constructor(value_storage_delete_copy_constructor &&) = default; // NOLINT
  335. };
  336. template <class Base> struct value_storage_delete_copy_assignment : Base // NOLINT
  337. {
  338. using Base::Base;
  339. using value_type = typename Base::value_type;
  340. value_storage_delete_copy_assignment() = default;
  341. value_storage_delete_copy_assignment(const value_storage_delete_copy_assignment &) = default;
  342. value_storage_delete_copy_assignment(value_storage_delete_copy_assignment &&) = default; // NOLINT
  343. value_storage_delete_copy_assignment &operator=(const value_storage_delete_copy_assignment &o) = delete;
  344. value_storage_delete_copy_assignment &operator=(value_storage_delete_copy_assignment &&o) = default; // NOLINT
  345. };
  346. template <class Base> struct value_storage_delete_move_assignment : Base // NOLINT
  347. {
  348. using Base::Base;
  349. using value_type = typename Base::value_type;
  350. value_storage_delete_move_assignment() = default;
  351. value_storage_delete_move_assignment(const value_storage_delete_move_assignment &) = default;
  352. value_storage_delete_move_assignment(value_storage_delete_move_assignment &&) = default; // NOLINT
  353. value_storage_delete_move_assignment &operator=(const value_storage_delete_move_assignment &o) = default;
  354. value_storage_delete_move_assignment &operator=(value_storage_delete_move_assignment &&o) = delete;
  355. };
  356. template <class Base> struct value_storage_delete_move_constructor : Base // NOLINT
  357. {
  358. using Base::Base;
  359. using value_type = typename Base::value_type;
  360. value_storage_delete_move_constructor() = default;
  361. value_storage_delete_move_constructor(const value_storage_delete_move_constructor &) = default;
  362. value_storage_delete_move_constructor(value_storage_delete_move_constructor &&) = delete;
  363. };
  364. template <class Base> struct value_storage_nontrivial_move_assignment : Base // NOLINT
  365. {
  366. using Base::Base;
  367. using value_type = typename Base::value_type;
  368. value_storage_nontrivial_move_assignment() = default;
  369. value_storage_nontrivial_move_assignment(const value_storage_nontrivial_move_assignment &) = default;
  370. value_storage_nontrivial_move_assignment(value_storage_nontrivial_move_assignment &&) = default; // NOLINT
  371. value_storage_nontrivial_move_assignment &operator=(const value_storage_nontrivial_move_assignment &o) = default;
  372. value_storage_nontrivial_move_assignment &operator=(value_storage_nontrivial_move_assignment &&o) noexcept(std::is_nothrow_move_assignable<value_type>::value) // NOLINT
  373. {
  374. if((this->_status & status_have_value) != 0 && (o._status & status_have_value) != 0)
  375. {
  376. this->_value = static_cast<value_type &&>(o._value); // NOLINT
  377. }
  378. else if((this->_status & status_have_value) != 0 && (o._status & status_have_value) == 0)
  379. {
  380. this->_value.~value_type(); // NOLINT
  381. }
  382. else if((this->_status & status_have_value) == 0 && (o._status & status_have_value) != 0)
  383. {
  384. new(&this->_value) value_type(static_cast<value_type &&>(o._value)); // NOLINT
  385. }
  386. this->_status = o._status;
  387. return *this;
  388. }
  389. };
  390. template <class Base> struct value_storage_nontrivial_copy_assignment : Base // NOLINT
  391. {
  392. using Base::Base;
  393. using value_type = typename Base::value_type;
  394. value_storage_nontrivial_copy_assignment() = default;
  395. value_storage_nontrivial_copy_assignment(const value_storage_nontrivial_copy_assignment &) = default;
  396. value_storage_nontrivial_copy_assignment(value_storage_nontrivial_copy_assignment &&) = default; // NOLINT
  397. value_storage_nontrivial_copy_assignment &operator=(value_storage_nontrivial_copy_assignment &&o) = default; // NOLINT
  398. value_storage_nontrivial_copy_assignment &operator=(const value_storage_nontrivial_copy_assignment &o) noexcept(std::is_nothrow_copy_assignable<value_type>::value)
  399. {
  400. if((this->_status & status_have_value) != 0 && (o._status & status_have_value) != 0)
  401. {
  402. this->_value = o._value; // NOLINT
  403. }
  404. else if((this->_status & status_have_value) != 0 && (o._status & status_have_value) == 0)
  405. {
  406. this->_value.~value_type(); // NOLINT
  407. }
  408. else if((this->_status & status_have_value) == 0 && (o._status & status_have_value) != 0)
  409. {
  410. new(&this->_value) value_type(o._value); // NOLINT
  411. }
  412. this->_status = o._status;
  413. return *this;
  414. }
  415. };
  416. // We don't actually need all of std::is_trivial<>, std::is_trivially_copyable<> is sufficient
  417. template <class T> using value_storage_select_trivality = std::conditional_t<std::is_trivially_copyable<devoid<T>>::value, value_storage_trivial<T>, value_storage_nontrivial<T>>;
  418. template <class T> using value_storage_select_move_constructor = std::conditional_t<std::is_move_constructible<devoid<T>>::value, value_storage_select_trivality<T>, value_storage_delete_move_constructor<value_storage_select_trivality<T>>>;
  419. template <class T> using value_storage_select_copy_constructor = std::conditional_t<std::is_copy_constructible<devoid<T>>::value, value_storage_select_move_constructor<T>, value_storage_delete_copy_constructor<value_storage_select_move_constructor<T>>>;
  420. template <class T>
  421. using value_storage_select_move_assignment = std::conditional_t<std::is_trivially_move_assignable<devoid<T>>::value, value_storage_select_copy_constructor<T>,
  422. std::conditional_t<std::is_move_assignable<devoid<T>>::value, value_storage_nontrivial_move_assignment<value_storage_select_copy_constructor<T>>, value_storage_delete_copy_assignment<value_storage_select_copy_constructor<T>>>>;
  423. template <class T>
  424. using value_storage_select_copy_assignment = std::conditional_t<std::is_trivially_copy_assignable<devoid<T>>::value, value_storage_select_move_assignment<T>,
  425. std::conditional_t<std::is_copy_assignable<devoid<T>>::value, value_storage_nontrivial_copy_assignment<value_storage_select_move_assignment<T>>, value_storage_delete_copy_assignment<value_storage_select_move_assignment<T>>>>;
  426. template <class T> using value_storage_select_impl = value_storage_select_copy_assignment<T>;
  427. #ifndef NDEBUG
  428. // Check is trivial in all ways except default constructibility
  429. // static_assert(std::is_trivial<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivial!");
  430. // static_assert(std::is_trivially_default_constructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially default constructible!");
  431. static_assert(std::is_trivially_copyable<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially copyable!");
  432. static_assert(std::is_trivially_assignable<value_storage_select_impl<int>, value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially assignable!");
  433. static_assert(std::is_trivially_destructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially destructible!");
  434. static_assert(std::is_trivially_copy_constructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially copy constructible!");
  435. static_assert(std::is_trivially_move_constructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially move constructible!");
  436. static_assert(std::is_trivially_copy_assignable<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially copy assignable!");
  437. static_assert(std::is_trivially_move_assignable<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially move assignable!");
  438. // Also check is standard layout
  439. static_assert(std::is_standard_layout<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not a standard layout type!");
  440. #endif
  441. } // namespace detail
  442. BOOST_OUTCOME_V2_NAMESPACE_END
  443. #endif