LCOV - code coverage report
Current view: top level - boost/capy - neunique_ptr.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.2 % 312 294
Test Date: 2025-12-15 05:33:30 Functions: 92.1 % 191 176

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/capy
       8              : //
       9              : 
      10              : #ifndef BOOST_CAPY_NEUNIQUE_PTR_HPP
      11              : #define BOOST_CAPY_NEUNIQUE_PTR_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/assert.hpp>
      15              : #include <cstddef>
      16              : #include <functional>
      17              : #include <memory>
      18              : #include <type_traits>
      19              : #include <utility>
      20              : 
      21              : namespace boost {
      22              : namespace capy {
      23              : 
      24              : namespace detail {
      25              : 
      26              : //----------------------------------------------------------
      27              : 
      28              : template<class U>
      29           22 : auto try_delete(U* p, int) noexcept
      30              :     -> decltype(sizeof(U), void())
      31              : {
      32           22 :     delete p;
      33           22 : }
      34              : 
      35              : template<class U>
      36            0 : void try_delete(U*, long) noexcept
      37              : {
      38              :     // Incomplete type - should never reach here
      39            0 :     std::terminate();
      40              : }
      41              : 
      42              : template<class U>
      43           10 : auto try_delete_array(U* p, int) noexcept
      44              :     -> decltype(sizeof(U), void())
      45              : {
      46           10 :     delete[] p;
      47           10 : }
      48              : 
      49              : template<class U>
      50              : void try_delete_array(U*, long) noexcept
      51              : {
      52              :     std::terminate();
      53              : }
      54              : 
      55              : /** Storage wrapper applying empty base optimization.
      56              : 
      57              :     When `T` is empty and non-final, inherits from it to
      58              :     apply EBO. Otherwise stores as a member.
      59              : */
      60              : #if __cplusplus >= 201402L
      61              : template<
      62              :     class T,
      63              :     bool = std::is_empty<T>::value && !std::is_final<T>::value>
      64              : #else
      65              : template<
      66              :     class T,
      67              :     bool = std::is_empty<T>::value>
      68              : #endif
      69              : struct ebo_storage
      70              : {
      71              :     T value_;
      72              : 
      73              :     ebo_storage() = default;
      74              : 
      75              :     explicit ebo_storage(T const& t)
      76              :         : value_(t)
      77              :     {
      78              :     }
      79              : 
      80            4 :     explicit ebo_storage(T&& t)
      81            4 :         : value_(std::move(t))
      82              :     {
      83            4 :     }
      84              : 
      85            4 :     T& get() noexcept { return value_; }
      86              :     T const& get() const noexcept { return value_; }
      87              : };
      88              : 
      89              : template<class T>
      90              : struct ebo_storage<T, true> : T
      91              : {
      92              :     ebo_storage() = default;
      93              : 
      94              :     explicit ebo_storage(T const& t)
      95              :         : T(t)
      96              :     {
      97              :     }
      98              : 
      99            6 :     explicit ebo_storage(T&& t)
     100            6 :         : T(std::move(t))
     101              :     {
     102            6 :     }
     103              : 
     104            6 :     T& get() noexcept { return *this; }
     105              :     T const& get() const noexcept { return *this; }
     106              : };
     107              : 
     108              : //----------------------------------------------------------
     109              : 
     110              : /** RAII scope guard for rollback on exception.
     111              : */
     112              : template<class F>
     113              : class scope_guard
     114              : {
     115              :     F f_;
     116              :     bool active_ = true;
     117              : 
     118              : public:
     119           17 :     explicit scope_guard(F f) noexcept
     120           17 :         : f_(std::move(f))
     121              :     {
     122           17 :     }
     123              : 
     124           17 :     ~scope_guard()
     125              :     {
     126           17 :         if(active_)
     127            0 :             f_();
     128           17 :     }
     129              : 
     130           17 :     void release() noexcept
     131              :     {
     132           17 :         active_ = false;
     133           17 :     }
     134              : 
     135              :     scope_guard(scope_guard&& other) noexcept
     136              :         : f_(std::move(other.f_))
     137              :         , active_(other.active_)
     138              :     {
     139              :         other.active_ = false;
     140              :     }
     141              : 
     142              :     scope_guard(scope_guard const&) = delete;
     143              :     scope_guard& operator=(scope_guard const&) = delete;
     144              :     scope_guard& operator=(scope_guard&&) = delete;
     145              : };
     146              : 
     147              : template<class F>
     148              : scope_guard<F>
     149           17 : make_scope_guard(F f) noexcept
     150              : {
     151           17 :     return scope_guard<F>(std::move(f));
     152              : }
     153              : 
     154              : //----------------------------------------------------------
     155              : 
     156              : /** Base class for type-erased control blocks.
     157              : 
     158              :     The control block stores the deleter and allocator,
     159              :     enabling type erasure and incomplete type support.
     160              : */
     161              : struct control_block_base
     162              : {
     163              :     virtual void destroy_and_deallocate() noexcept = 0;
     164              : 
     165              : protected:
     166              :     ~control_block_base() = default;
     167              : };
     168              : 
     169              : //----------------------------------------------------------
     170              : 
     171              : /** Control block storing pointer, deleter, and allocator.
     172              : 
     173              :     Used when constructing from a raw pointer with a
     174              :     custom deleter and/or allocator.
     175              : */
     176              : template<class T, class D, class A>
     177              : struct control_block_pda final
     178              :     : control_block_base
     179              :     , private ebo_storage<
     180              :         typename std::allocator_traits<A>::
     181              :             template rebind_alloc<control_block_pda<T, D, A>>>
     182              : {
     183              :     using alloc_type = typename std::allocator_traits<A>::
     184              :         template rebind_alloc<control_block_pda>;
     185              :     using alloc_traits = std::allocator_traits<alloc_type>;
     186              :     using alloc_storage = ebo_storage<alloc_type>;
     187              : 
     188              :     T* ptr;
     189              :     D deleter;
     190              : 
     191            7 :     alloc_type& get_alloc() noexcept
     192              :     {
     193            7 :         return alloc_storage::get();
     194              :     }
     195              : 
     196            7 :     control_block_pda(T* p, D d, A const& a)
     197            9 :         : alloc_storage(alloc_type(a))
     198            7 :         , ptr(p)
     199           17 :         , deleter(std::move(d))
     200              :     {
     201            7 :     }
     202              : 
     203            7 :     void destroy_and_deallocate() noexcept override
     204              :     {
     205            7 :         T* p = ptr;
     206            7 :         D del = std::move(deleter);
     207            7 :         alloc_type a = std::move(get_alloc());
     208            7 :         this->~control_block_pda();
     209            2 :         alloc_traits::deallocate(a, this, 1);
     210            7 :         if(p)
     211            7 :             del(p);
     212            7 :     }
     213              : };
     214              : 
     215              : //----------------------------------------------------------
     216              : 
     217              : /** Control block with embedded object storage.
     218              : 
     219              :     Used by allocate_neunique to store the object
     220              :     inline with the control block.
     221              : */
     222              : template<class T, class A>
     223              : struct control_block_embedded final
     224              :     : control_block_base
     225              :     , private ebo_storage<
     226              :         typename std::allocator_traits<A>::
     227              :             template rebind_alloc<control_block_embedded<T, A>>>
     228              : {
     229              :     using alloc_type = typename std::allocator_traits<A>::
     230              :         template rebind_alloc<control_block_embedded>;
     231              :     using alloc_traits = std::allocator_traits<alloc_type>;
     232              :     using alloc_storage = ebo_storage<alloc_type>;
     233              : 
     234              :     alignas(T) unsigned char storage[sizeof(T)];
     235              : 
     236            2 :     alloc_type& get_alloc() noexcept
     237              :     {
     238            2 :         return alloc_storage::get();
     239              :     }
     240              : 
     241            4 :     T* get() noexcept
     242              :     {
     243            4 :         return reinterpret_cast<T*>(storage);
     244              :     }
     245              : 
     246              :     template<class... Args>
     247            2 :     explicit control_block_embedded(A const& a, Args&&... args)
     248            3 :         : alloc_storage(alloc_type(a))
     249              :     {
     250            3 :         ::new(static_cast<void*>(storage)) T(
     251            2 :             std::forward<Args>(args)...);
     252            2 :     }
     253              : 
     254            2 :     void destroy_and_deallocate() noexcept override
     255              :     {
     256            2 :         get()->~T();
     257            2 :         alloc_type a = std::move(get_alloc());
     258            2 :         this->~control_block_embedded();
     259            1 :         alloc_traits::deallocate(a, this, 1);
     260            2 :     }
     261              : };
     262              : 
     263              : //----------------------------------------------------------
     264              : 
     265              : /** Control block for arrays with embedded storage.
     266              : 
     267              :     Used by allocate_neunique for array types.
     268              : */
     269              : template<class T, class A>
     270              : struct control_block_array final
     271              :     : control_block_base
     272              :     , private ebo_storage<
     273              :         typename std::allocator_traits<A>::
     274              :             template rebind_alloc<control_block_array<T, A>>>
     275              : {
     276              :     using alloc_type = typename std::allocator_traits<A>::
     277              :         template rebind_alloc<control_block_array>;
     278              :     using alloc_traits = std::allocator_traits<alloc_type>;
     279              :     using alloc_storage = ebo_storage<alloc_type>;
     280              : 
     281              :     std::size_t size;
     282              :     // Flexible array member follows
     283              : 
     284            1 :     explicit control_block_array(A const& a)
     285            2 :         : alloc_storage(alloc_type(a))
     286            1 :         , size(0)
     287              :     {
     288            1 :     }
     289              : 
     290            1 :     alloc_type& get_alloc() noexcept
     291              :     {
     292            1 :         return alloc_storage::get();
     293              :     }
     294              : 
     295            2 :     T* get() noexcept
     296              :     {
     297              :         return reinterpret_cast<T*>(
     298              :             reinterpret_cast<unsigned char*>(this) +
     299            2 :             sizeof(control_block_array));
     300              :     }
     301              : 
     302            2 :     static std::size_t storage_size(std::size_t n) noexcept
     303              :     {
     304            2 :         return sizeof(control_block_array) + sizeof(T) * n;
     305              :     }
     306              : 
     307            1 :     void destroy_and_deallocate() noexcept override
     308              :     {
     309            1 :         T* p = get();
     310            4 :         for(std::size_t i = size; i > 0; --i)
     311            3 :             p[i - 1].~T();
     312            1 :         alloc_type a = std::move(get_alloc());
     313            1 :         std::size_t sz = size;
     314            1 :         this->~control_block_array();
     315            1 :         std::size_t units = (storage_size(sz) +
     316            1 :             sizeof(control_block_array) - 1) /
     317              :             sizeof(control_block_array);
     318            1 :         alloc_traits::deallocate(a,
     319              :             reinterpret_cast<control_block_array*>(this), units);
     320            1 :     }
     321              : };
     322              : 
     323              : } // detail
     324              : 
     325              : //----------------------------------------------------------
     326              : 
     327              : template<class T>
     328              : class neunique_ptr;
     329              : 
     330              : template<class T, class A, class... Args>
     331              : typename std::enable_if<
     332              :     !std::is_array<T>::value,
     333              :     neunique_ptr<T>>::type
     334              : allocate_neunique(A const& a, Args&&... args);
     335              : 
     336              : template<class T, class A>
     337              : typename std::enable_if<
     338              :     std::is_array<T>::value && std::extent<T>::value == 0,
     339              :     neunique_ptr<T>>::type
     340              : allocate_neunique(A const& a, std::size_t n);
     341              : 
     342              : //----------------------------------------------------------
     343              : 
     344              : /** A smart pointer with unique ownership, type-erased deleter,
     345              :     and allocator support.
     346              : 
     347              :     This class provides unique ownership semantics similar to
     348              :     `std::unique_ptr`, combined with features from `std::shared_ptr`:
     349              : 
     350              :     @li Type-erased deleters - pointers with different deleters
     351              :         share the same static type
     352              :     @li Allocator support - custom allocators for the control block
     353              :     @li Incomplete types - the destructor is captured at construction
     354              :     @li Aliasing - the stored pointer can differ from the owned pointer
     355              : 
     356              :     The implementation uses a control block to store the deleter
     357              :     and allocator, similar to `std::shared_ptr` but without
     358              :     reference counting.
     359              : 
     360              :     @par Control Block Elision
     361              : 
     362              :     When constructed from a raw pointer without a custom deleter
     363              :     or allocator, no control block is allocated. The pointer is
     364              :     deleted directly using `delete`. This optimization requires
     365              :     the type to be complete at destruction time.
     366              : 
     367              :     @par Size
     368              : 
     369              :     `sizeof(neunique_ptr<T>)` is two pointers (16 bytes on 64-bit).
     370              : 
     371              :     @par Thread Safety
     372              : 
     373              :     Distinct `neunique_ptr` objects may be accessed concurrently.
     374              :     A single `neunique_ptr` object may not be accessed concurrently
     375              :     from multiple threads.
     376              : 
     377              :     @tparam T The element type. May be incomplete at declaration.
     378              :         For arrays, use `neunique_ptr<T[]>`.
     379              : 
     380              :     @see make_neunique, allocate_neunique
     381              : */
     382              : template<class T>
     383              : class neunique_ptr
     384              : {
     385              :     template<class U> friend class neunique_ptr;
     386              : 
     387              :     template<class U, class A, class... Args>
     388              :     friend typename std::enable_if<
     389              :         !std::is_array<U>::value,
     390              :         neunique_ptr<U>>::type
     391              :     allocate_neunique(A const& a, Args&&... args);
     392              : 
     393              :     using control_block = detail::control_block_base;
     394              : 
     395              :     T* ptr_ = nullptr;
     396              :     control_block* cb_ = nullptr;
     397              : 
     398              :     template<class D, class A>
     399            6 :     void init_pda(T* p, D d, A const& a)
     400              :     {
     401              :         using cb_type = detail::control_block_pda<T, D, A>;
     402              :         using alloc_type = typename cb_type::alloc_type;
     403              :         using alloc_traits = std::allocator_traits<alloc_type>;
     404              : 
     405            2 :         alloc_type alloc(a);
     406            6 :         cb_type* cb = alloc_traits::allocate(alloc, 1);
     407            6 :         auto guard = detail::make_scope_guard(
     408            0 :             [&]{ alloc_traits::deallocate(alloc, cb, 1); });
     409            6 :         ::new(static_cast<void*>(cb)) cb_type(p, std::move(d), a);
     410            6 :         guard.release();
     411            6 :         ptr_ = p;
     412            6 :         cb_ = cb;
     413           10 :     }
     414              : 
     415              : public:
     416              :     /** The pointer type.
     417              :     */
     418              :     using pointer = T*;
     419              : 
     420              :     /** The element type.
     421              :     */
     422              :     using element_type = T;
     423              : 
     424              :     //------------------------------------------------------
     425              :     //
     426              :     // Constructors
     427              :     //
     428              :     //------------------------------------------------------
     429              : 
     430              :     /** Construct an empty pointer.
     431              : 
     432              :         @post `get() == nullptr`
     433              :     */
     434              :     constexpr neunique_ptr() noexcept = default;
     435              : 
     436              :     /** Construct an empty pointer from nullptr.
     437              : 
     438              :         @post `get() == nullptr`
     439              :     */
     440              :     constexpr neunique_ptr(std::nullptr_t) noexcept {}
     441              : 
     442              :     /** Construct from a raw pointer.
     443              : 
     444              :         Takes ownership of `p` using `delete`. No control
     445              :         block is allocated. The type must be complete at
     446              :         destruction time.
     447              : 
     448              :         @param p Pointer to take ownership of, or nullptr.
     449              : 
     450              :         @post `get() == p`
     451              :     */
     452           21 :     explicit neunique_ptr(pointer p) noexcept
     453           21 :         : ptr_(p)
     454              :     {
     455           21 :     }
     456              : 
     457              :     /** Construct from a raw pointer with custom deleter.
     458              : 
     459              :         Takes ownership of `p` using the specified deleter
     460              :         and the default allocator.
     461              : 
     462              :         @param p Pointer to take ownership of, or nullptr.
     463              :         @param d Deleter callable as `d(p)`.
     464              : 
     465              :         @throws std::bad_alloc if control block allocation fails.
     466              :             If an exception is thrown, `d(p)` is called.
     467              : 
     468              :         @post `get() == p`
     469              :     */
     470              :     template<class D>
     471            4 :     neunique_ptr(pointer p, D d)
     472            4 :         : neunique_ptr(p, std::move(d), std::allocator<T>{})
     473              :     {
     474            4 :     }
     475              : 
     476              :     /** Construct from a raw pointer with custom deleter and allocator.
     477              : 
     478              :         Takes ownership of `p` using the specified deleter.
     479              :         The allocator is used to allocate the control block.
     480              : 
     481              :         @param p Pointer to take ownership of, or nullptr.
     482              :         @param d Deleter callable as `d(p)`.
     483              :         @param a Allocator for control block allocation.
     484              : 
     485              :         @throws Any exception thrown by control block allocation.
     486              :             If an exception is thrown, `d(p)` is called.
     487              : 
     488              :         @post `get() == p`
     489              :     */
     490              :     template<class D, class A>
     491            6 :     neunique_ptr(pointer p, D d, A const& a)
     492            6 :     {
     493            6 :         if(!p)
     494            0 :             return;
     495            6 :         auto guard = detail::make_scope_guard(
     496            0 :             [&]{ d(p); });
     497            6 :         init_pda(p, std::move(d), a);
     498            6 :         guard.release();
     499            6 :     }
     500              : 
     501              :     /** Aliasing constructor.
     502              : 
     503              :         Constructs a `neunique_ptr` that stores `p` but shares
     504              :         ownership with `other`. After construction, `get() == p`
     505              :         and `other` is empty.
     506              : 
     507              :         This allows a `neunique_ptr` to point to a subobject
     508              :         of the owned object.
     509              : 
     510              :         @note If `other` has no control block (was constructed
     511              :             from a raw pointer without a deleter), the behavior
     512              :             is undefined unless `p` equals `other.get()`.
     513              : 
     514              :         @param other Pointer to transfer ownership from.
     515              :         @param p Pointer to store (typically to a subobject
     516              :             of the object owned by `other`).
     517              : 
     518              :         @post `get() == p`
     519              :         @post `other.get() == nullptr`
     520              :     */
     521              :     template<class U>
     522            1 :     neunique_ptr(neunique_ptr<U>&& other, pointer p) noexcept
     523            1 :         : ptr_(p)
     524            1 :         , cb_(other.cb_)
     525              :     {
     526              :         // aliasing requires control block; use allocate_neunique
     527            1 :         BOOST_ASSERT((other.cb_ != nullptr || other.ptr_ == nullptr));
     528            1 :         other.ptr_ = nullptr;
     529            1 :         other.cb_ = nullptr;
     530            1 :     }
     531              :     /** Move constructor.
     532              : 
     533              :         Takes ownership from `other`. After construction,
     534              :         `other` is empty.
     535              : 
     536              :         @param other Pointer to move from.
     537              : 
     538              :         @post `other.get() == nullptr`
     539              :     */
     540            2 :     neunique_ptr(neunique_ptr&& other) noexcept
     541            2 :         : ptr_(other.ptr_)
     542            2 :         , cb_(other.cb_)
     543              :     {
     544            2 :         other.ptr_ = nullptr;
     545            2 :         other.cb_ = nullptr;
     546            2 :     }
     547              : 
     548              :     /** Converting move constructor.
     549              : 
     550              :         Takes ownership from `other`. After construction,
     551              :         `other` is empty. Participates in overload resolution
     552              :         only if `U*` is convertible to `T*`.
     553              : 
     554              :         @param other Pointer to move from.
     555              : 
     556              :         @post `other.get() == nullptr`
     557              :     */
     558              :     template<class U, class = typename std::enable_if<
     559              :         std::is_convertible<U*, T*>::value>::type>
     560            1 :     neunique_ptr(neunique_ptr<U>&& other) noexcept
     561            1 :         : ptr_(other.ptr_)
     562            1 :         , cb_(other.cb_)
     563              :     {
     564            1 :         other.ptr_ = nullptr;
     565            1 :         other.cb_ = nullptr;
     566            1 :     }
     567              : 
     568              :     neunique_ptr(neunique_ptr const&) = delete;
     569              :     neunique_ptr& operator=(neunique_ptr const&) = delete;
     570              : 
     571              :     //------------------------------------------------------
     572              :     //
     573              :     // Destructor
     574              :     //
     575              :     //------------------------------------------------------
     576              : 
     577              :     /** Destructor.
     578              : 
     579              :         Destroys the owned object using the stored deleter
     580              :         and deallocates the control block using the stored
     581              :         allocator. If no control block exists, uses `delete`.
     582              :     */
     583           41 :     ~neunique_ptr()
     584              :     {
     585           41 :         if(cb_)
     586            6 :             cb_->destroy_and_deallocate();
     587           35 :         else if(ptr_)
     588           19 :             detail::try_delete(ptr_, 0);
     589           41 :     }
     590              : 
     591              :     //------------------------------------------------------
     592              :     //
     593              :     // Assignment
     594              :     //
     595              :     //------------------------------------------------------
     596              : 
     597              :     /** Move assignment.
     598              : 
     599              :         Releases the currently owned object and takes
     600              :         ownership from `other`.
     601              : 
     602              :         @param other Pointer to move from.
     603              : 
     604              :         @return `*this`
     605              : 
     606              :         @post `other.get() == nullptr`
     607              :     */
     608            2 :     neunique_ptr& operator=(neunique_ptr&& other) noexcept
     609              :     {
     610            2 :         if(this != &other)
     611              :         {
     612            1 :             if(cb_)
     613            0 :                 cb_->destroy_and_deallocate();
     614            1 :             else if(ptr_)
     615            1 :                 detail::try_delete(ptr_, 0);
     616            1 :             ptr_ = other.ptr_;
     617            1 :             cb_ = other.cb_;
     618            1 :             other.ptr_ = nullptr;
     619            1 :             other.cb_ = nullptr;
     620              :         }
     621            2 :         return *this;
     622              :     }
     623              : 
     624              :     /** Converting move assignment.
     625              : 
     626              :         Releases the currently owned object and takes
     627              :         ownership from `other`. Participates in overload
     628              :         resolution only if `U*` is convertible to `T*`.
     629              : 
     630              :         @param other Pointer to move from.
     631              : 
     632              :         @return `*this`
     633              : 
     634              :         @post `other.get() == nullptr`
     635              :     */
     636              :     template<class U, class = typename std::enable_if<
     637              :         std::is_convertible<U*, T*>::value>::type>
     638            1 :     neunique_ptr& operator=(neunique_ptr<U>&& other) noexcept
     639              :     {
     640            1 :         if(cb_)
     641            0 :             cb_->destroy_and_deallocate();
     642            1 :         else if(ptr_)
     643            0 :             delete ptr_;
     644            1 :         ptr_ = other.ptr_;
     645            1 :         cb_ = other.cb_;
     646            1 :         other.ptr_ = nullptr;
     647            1 :         other.cb_ = nullptr;
     648            1 :         return *this;
     649              :     }
     650              : 
     651              :     /** Assign nullptr.
     652              : 
     653              :         Releases the currently owned object.
     654              : 
     655              :         @return `*this`
     656              : 
     657              :         @post `get() == nullptr`
     658              :     */
     659            1 :     neunique_ptr& operator=(std::nullptr_t) noexcept
     660              :     {
     661            1 :         reset();
     662            1 :         return *this;
     663              :     }
     664              : 
     665              :     //------------------------------------------------------
     666              :     //
     667              :     // Modifiers
     668              :     //
     669              :     //------------------------------------------------------
     670              : 
     671              :     /** Replace the owned object.
     672              : 
     673              :         Releases the currently owned object and takes
     674              :         ownership of `p` using `delete`. No control block
     675              :         is allocated.
     676              : 
     677              :         @param p Pointer to take ownership of, or nullptr.
     678              : 
     679              :         @post `get() == p`
     680              :     */
     681            5 :     void reset(pointer p = nullptr) noexcept
     682              :     {
     683            5 :         if(cb_)
     684            2 :             cb_->destroy_and_deallocate();
     685            3 :         else if(ptr_)
     686            2 :             detail::try_delete(ptr_, 0);
     687            5 :         ptr_ = p;
     688            5 :         cb_ = nullptr;
     689            5 :     }
     690              : 
     691              :     /** Replace the owned object with custom deleter.
     692              : 
     693              :         Releases the currently owned object and takes
     694              :         ownership of `p` using the specified deleter.
     695              : 
     696              :         @param p Pointer to take ownership of, or nullptr.
     697              :         @param d Deleter callable as `d(p)`.
     698              : 
     699              :         @throws Any exception thrown by control block allocation.
     700              : 
     701              :         @post `get() == p`
     702              :     */
     703              :     template<class D>
     704            1 :     void reset(pointer p, D d)
     705              :     {
     706            1 :         neunique_ptr(p, std::move(d)).swap(*this);
     707            1 :     }
     708              : 
     709              :     /** Replace the owned object with custom deleter and allocator.
     710              : 
     711              :         Releases the currently owned object and takes
     712              :         ownership of `p` using the specified deleter and
     713              :         allocator.
     714              : 
     715              :         @param p Pointer to take ownership of, or nullptr.
     716              :         @param d Deleter callable as `d(p)`.
     717              :         @param a Allocator for control block allocation.
     718              : 
     719              :         @throws Any exception thrown by control block allocation.
     720              : 
     721              :         @post `get() == p`
     722              :     */
     723              :     template<class D, class A>
     724            1 :     void reset(pointer p, D d, A const& a)
     725              :     {
     726            1 :         neunique_ptr(p, std::move(d), a).swap(*this);
     727            1 :     }
     728              : 
     729              :     /** Swap with another pointer.
     730              : 
     731              :         @param other Pointer to swap with.
     732              :     */
     733            4 :     void swap(neunique_ptr& other) noexcept
     734              :     {
     735            4 :         std::swap(ptr_, other.ptr_);
     736            4 :         std::swap(cb_, other.cb_);
     737            4 :     }
     738              : 
     739              :     //------------------------------------------------------
     740              :     //
     741              :     // Observers
     742              :     //
     743              :     //------------------------------------------------------
     744              : 
     745              :     /** Return the stored pointer.
     746              : 
     747              :         @return The stored pointer, or nullptr if empty.
     748              :     */
     749           43 :     pointer get() const noexcept
     750              :     {
     751           43 :         return ptr_;
     752              :     }
     753              : 
     754              :     /** Check if non-empty.
     755              : 
     756              :         @return `true` if `get() != nullptr`.
     757              :     */
     758           35 :     explicit operator bool() const noexcept
     759              :     {
     760           35 :         return ptr_ != nullptr;
     761              :     }
     762              : 
     763              :     /** Dereference the pointer.
     764              : 
     765              :         @pre `get() != nullptr`
     766              : 
     767              :         @return Reference to the pointed-to object.
     768              :     */
     769              :     typename std::add_lvalue_reference<T>::type
     770           15 :     operator*() const noexcept
     771              :     {
     772           15 :         return *ptr_;
     773              :     }
     774              : 
     775              :     /** Member access.
     776              : 
     777              :         @pre `get() != nullptr`
     778              : 
     779              :         @return The stored pointer.
     780              :     */
     781            6 :     pointer operator->() const noexcept
     782              :     {
     783            6 :         return ptr_;
     784              :     }
     785              : };
     786              : 
     787              : //----------------------------------------------------------
     788              : 
     789              : /** A smart pointer with unique ownership for arrays.
     790              : 
     791              :     Array specialization of @ref neunique_ptr. Provides
     792              :     `operator[]` instead of `operator*` and `operator->`.
     793              : 
     794              :     @tparam T The element type (without `[]`).
     795              : 
     796              :     @see neunique_ptr, make_neunique, allocate_neunique
     797              : */
     798              : template<class T>
     799              : class neunique_ptr<T[]>
     800              : {
     801              :     template<class U> friend class neunique_ptr;
     802              : 
     803              :     template<class U, class A>
     804              :     friend typename std::enable_if<
     805              :         std::is_array<U>::value && std::extent<U>::value == 0,
     806              :         neunique_ptr<U>>::type
     807              :     allocate_neunique(A const& a, std::size_t n);
     808              : 
     809              :     using control_block = detail::control_block_base;
     810              : 
     811              :     T* ptr_ = nullptr;
     812              :     control_block* cb_ = nullptr;
     813              : 
     814              :     template<class D, class A>
     815            1 :     void init_pda(T* p, D d, A const& a)
     816              :     {
     817              :         using cb_type = detail::control_block_pda<T, D, A>;
     818              :         using alloc_type = typename cb_type::alloc_type;
     819              :         using alloc_traits = std::allocator_traits<alloc_type>;
     820              : 
     821              :         alloc_type alloc(a);
     822            1 :         cb_type* cb = alloc_traits::allocate(alloc, 1);
     823            1 :         auto guard = detail::make_scope_guard(
     824            0 :             [&]{ alloc_traits::deallocate(alloc, cb, 1); });
     825            1 :         ::new(static_cast<void*>(cb)) cb_type(p, std::move(d), a);
     826            1 :         guard.release();
     827            1 :         ptr_ = p;
     828            1 :         cb_ = cb;
     829            2 :     }
     830              : 
     831              : public:
     832              :     /** The pointer type.
     833              :     */
     834              :     using pointer = T*;
     835              : 
     836              :     /** The element type.
     837              :     */
     838              :     using element_type = T;
     839              : 
     840              :     //------------------------------------------------------
     841              :     //
     842              :     // Constructors
     843              :     //
     844              :     //------------------------------------------------------
     845              : 
     846              :     /** Construct an empty pointer.
     847              : 
     848              :         @post `get() == nullptr`
     849              :     */
     850              :     constexpr neunique_ptr() noexcept = default;
     851              : 
     852              :     /** Construct an empty pointer from nullptr.
     853              : 
     854              :         @post `get() == nullptr`
     855              :     */
     856              :     constexpr neunique_ptr(std::nullptr_t) noexcept {}
     857              : 
     858              :     /** Construct from a raw pointer.
     859              : 
     860              :         Takes ownership of `p` using `delete[]`. No control
     861              :         block is allocated. The type must be complete at
     862              :         destruction time.
     863              : 
     864              :         @param p Pointer to take ownership of, or nullptr.
     865              : 
     866              :         @post `get() == p`
     867              :     */
     868              :     template<class U, class = typename std::enable_if<
     869              :         std::is_same<U, T*>::value ||
     870              :         std::is_same<U, std::nullptr_t>::value>::type>
     871            9 :     explicit neunique_ptr(U p) noexcept
     872            9 :         : ptr_(p)
     873              :     {
     874            9 :     }
     875              : 
     876              :     /** Construct from a raw pointer with custom deleter.
     877              : 
     878              :         Takes ownership of `p` using the specified deleter
     879              :         and the default allocator.
     880              : 
     881              :         @param p Pointer to take ownership of, or nullptr.
     882              :         @param d Deleter callable as `d(p)`.
     883              : 
     884              :         @throws std::bad_alloc if control block allocation fails.
     885              :             If an exception is thrown, `d(p)` is called.
     886              : 
     887              :         @post `get() == p`
     888              :     */
     889              :     template<class D>
     890            1 :     neunique_ptr(pointer p, D d)
     891            1 :         : neunique_ptr(p, std::move(d), std::allocator<T>{})
     892              :     {
     893            1 :     }
     894              : 
     895              :     /** Construct from a raw pointer with custom deleter and allocator.
     896              : 
     897              :         Takes ownership of `p` using the specified deleter.
     898              :         The allocator is used to allocate the control block.
     899              : 
     900              :         @param p Pointer to take ownership of, or nullptr.
     901              :         @param d Deleter callable as `d(p)`.
     902              :         @param a Allocator for control block allocation.
     903              : 
     904              :         @throws Any exception thrown by control block allocation.
     905              :             If an exception is thrown, `d(p)` is called.
     906              : 
     907              :         @post `get() == p`
     908              :     */
     909              :     template<class D, class A>
     910            1 :     neunique_ptr(pointer p, D d, A const& a)
     911            1 :     {
     912            1 :         if(!p)
     913            0 :             return;
     914            1 :         auto guard = detail::make_scope_guard(
     915            0 :             [&]{ d(p); });
     916            1 :         init_pda(p, std::move(d), a);
     917            1 :         guard.release();
     918            1 :     }
     919              : 
     920              :     /** Aliasing constructor.
     921              : 
     922              :         Constructs a `neunique_ptr` that stores `p` but shares
     923              :         ownership with `other`. After construction, `get() == p`
     924              :         and `other` is empty.
     925              : 
     926              :         @note If `other` has no control block (was constructed
     927              :             from a raw pointer without a deleter), the behavior
     928              :             is undefined unless `p` equals `other.get()`.
     929              : 
     930              :         @param other Pointer to transfer ownership from.
     931              :         @param p Pointer to store.
     932              : 
     933              :         @post `get() == p`
     934              :         @post `other.get() == nullptr`
     935              :     */
     936              :     template<class U>
     937              :     neunique_ptr(neunique_ptr<U>&& other, pointer p) noexcept
     938              :         : ptr_(p)
     939              :         , cb_(other.cb_)
     940              :     {
     941              :         // aliasing requires control block; use allocate_neunique
     942              :         BOOST_ASSERT((other.cb_ != nullptr || other.ptr_ == nullptr));
     943              :         other.ptr_ = nullptr;
     944              :         other.cb_ = nullptr;
     945              :     }
     946              : 
     947              :     /** Move constructor.
     948              : 
     949              :         Takes ownership from `other`. After construction,
     950              :         `other` is empty.
     951              : 
     952              :         @param other Pointer to move from.
     953              : 
     954              :         @post `other.get() == nullptr`
     955              :     */
     956            1 :     neunique_ptr(neunique_ptr&& other) noexcept
     957            1 :         : ptr_(other.ptr_)
     958            1 :         , cb_(other.cb_)
     959              :     {
     960            1 :         other.ptr_ = nullptr;
     961            1 :         other.cb_ = nullptr;
     962            1 :     }
     963              : 
     964              :     neunique_ptr(neunique_ptr const&) = delete;
     965              :     neunique_ptr& operator=(neunique_ptr const&) = delete;
     966              : 
     967              :     //------------------------------------------------------
     968              :     //
     969              :     // Destructor
     970              :     //
     971              :     //------------------------------------------------------
     972              : 
     973              :     /** Destructor.
     974              : 
     975              :         Destroys the owned array using the stored deleter
     976              :         and deallocates the control block. If no control
     977              :         block exists, uses `delete[]`.
     978              :     */
     979           15 :     ~neunique_ptr()
     980              :     {
     981           15 :         if(cb_)
     982            2 :             cb_->destroy_and_deallocate();
     983           13 :         else if(ptr_)
     984            8 :             detail::try_delete_array(ptr_, 0);
     985           15 :     }
     986              : 
     987              :     //------------------------------------------------------
     988              :     //
     989              :     // Assignment
     990              :     //
     991              :     //------------------------------------------------------
     992              : 
     993              :     /** Move assignment.
     994              : 
     995              :         Releases the currently owned array and takes
     996              :         ownership from `other`.
     997              : 
     998              :         @param other Pointer to move from.
     999              : 
    1000              :         @return `*this`
    1001              : 
    1002              :         @post `other.get() == nullptr`
    1003              :     */
    1004            1 :     neunique_ptr& operator=(neunique_ptr&& other) noexcept
    1005              :     {
    1006            1 :         if(this != &other)
    1007              :         {
    1008            1 :             if(cb_)
    1009            0 :                 cb_->destroy_and_deallocate();
    1010            1 :             else if(ptr_)
    1011            1 :                 detail::try_delete_array(ptr_, 0);
    1012            1 :             ptr_ = other.ptr_;
    1013            1 :             cb_ = other.cb_;
    1014            1 :             other.ptr_ = nullptr;
    1015            1 :             other.cb_ = nullptr;
    1016              :         }
    1017            1 :         return *this;
    1018              :     }
    1019              : 
    1020              :     /** Assign nullptr.
    1021              : 
    1022              :         Releases the currently owned array.
    1023              : 
    1024              :         @return `*this`
    1025              : 
    1026              :         @post `get() == nullptr`
    1027              :     */
    1028              :     neunique_ptr& operator=(std::nullptr_t) noexcept
    1029              :     {
    1030              :         reset();
    1031              :         return *this;
    1032              :     }
    1033              : 
    1034              :     //------------------------------------------------------
    1035              :     //
    1036              :     // Modifiers
    1037              :     //
    1038              :     //------------------------------------------------------
    1039              : 
    1040              :     /** Replace the owned array.
    1041              : 
    1042              :         Releases the currently owned array.
    1043              : 
    1044              :         @post `get() == nullptr`
    1045              :     */
    1046            1 :     void reset() noexcept
    1047              :     {
    1048            1 :         if(cb_)
    1049            0 :             cb_->destroy_and_deallocate();
    1050            1 :         else if(ptr_)
    1051            1 :             detail::try_delete_array(ptr_, 0);
    1052            1 :         ptr_ = nullptr;
    1053            1 :         cb_ = nullptr;
    1054            1 :     }
    1055              : 
    1056              :     /** Replace the owned array.
    1057              : 
    1058              :         Releases the currently owned array and takes
    1059              :         ownership of `p` using `delete[]`. No control
    1060              :         block is allocated.
    1061              : 
    1062              :         @param p Pointer to take ownership of, or nullptr.
    1063              : 
    1064              :         @post `get() == p`
    1065              :     */
    1066              :     template<class U, class = typename std::enable_if<
    1067              :         std::is_same<U, T*>::value ||
    1068              :         std::is_same<U, std::nullptr_t>::value>::type>
    1069            1 :     void reset(U p) noexcept
    1070              :     {
    1071            1 :         if(cb_)
    1072            0 :             cb_->destroy_and_deallocate();
    1073            1 :         else if(ptr_)
    1074            0 :             delete[] ptr_;
    1075            1 :         ptr_ = p;
    1076            1 :         cb_ = nullptr;
    1077            1 :     }
    1078              : 
    1079              :     /** Replace the owned array with custom deleter.
    1080              : 
    1081              :         Releases the currently owned array and takes
    1082              :         ownership of `p` using the specified deleter.
    1083              : 
    1084              :         @param p Pointer to take ownership of, or nullptr.
    1085              :         @param d Deleter callable as `d(p)`.
    1086              : 
    1087              :         @throws Any exception thrown by control block allocation.
    1088              : 
    1089              :         @post `get() == p`
    1090              :     */
    1091              :     template<class D>
    1092              :     void reset(pointer p, D d)
    1093              :     {
    1094              :         neunique_ptr(p, std::move(d)).swap(*this);
    1095              :     }
    1096              : 
    1097              :     /** Replace the owned array with custom deleter and allocator.
    1098              : 
    1099              :         Releases the currently owned array and takes
    1100              :         ownership of `p` using the specified deleter.
    1101              : 
    1102              :         @param p Pointer to take ownership of, or nullptr.
    1103              :         @param d Deleter callable as `d(p)`.
    1104              :         @param a Allocator for control block allocation.
    1105              : 
    1106              :         @throws Any exception thrown by control block allocation.
    1107              : 
    1108              :         @post `get() == p`
    1109              :     */
    1110              :     template<class D, class A>
    1111              :     void reset(pointer p, D d, A const& a)
    1112              :     {
    1113              :         neunique_ptr(p, std::move(d), a).swap(*this);
    1114              :     }
    1115              : 
    1116              :     /** Swap with another pointer.
    1117              : 
    1118              :         @param other Pointer to swap with.
    1119              :     */
    1120            1 :     void swap(neunique_ptr& other) noexcept
    1121              :     {
    1122            1 :         std::swap(ptr_, other.ptr_);
    1123            1 :         std::swap(cb_, other.cb_);
    1124            1 :     }
    1125              : 
    1126              :     //------------------------------------------------------
    1127              :     //
    1128              :     // Observers
    1129              :     //
    1130              :     //------------------------------------------------------
    1131              : 
    1132              :     /** Return the stored pointer.
    1133              : 
    1134              :         @return The stored pointer, or nullptr if empty.
    1135              :     */
    1136           12 :     pointer get() const noexcept
    1137              :     {
    1138           12 :         return ptr_;
    1139              :     }
    1140              : 
    1141              :     /** Check if non-empty.
    1142              : 
    1143              :         @return `true` if `get() != nullptr`.
    1144              :     */
    1145           15 :     explicit operator bool() const noexcept
    1146              :     {
    1147           15 :         return ptr_ != nullptr;
    1148              :     }
    1149              : 
    1150              :     /** Access array element.
    1151              : 
    1152              :         @param i Index of element to access.
    1153              : 
    1154              :         @pre `get() != nullptr`
    1155              :         @pre `i` is within bounds.
    1156              : 
    1157              :         @return Reference to element at index `i`.
    1158              :     */
    1159           24 :     T& operator[](std::size_t i) const noexcept
    1160              :     {
    1161           24 :         return ptr_[i];
    1162              :     }
    1163              : };
    1164              : 
    1165              : //----------------------------------------------------------
    1166              : //
    1167              : // Free functions
    1168              : //
    1169              : //----------------------------------------------------------
    1170              : 
    1171              : /** Create a neunique_ptr for a single object.
    1172              : 
    1173              :     Allocates and constructs an object of type `T` using
    1174              :     `new`. No control block is allocated.
    1175              : 
    1176              :     @param args Arguments forwarded to `T`'s constructor.
    1177              : 
    1178              :     @return A `neunique_ptr<T>` owning the new object.
    1179              : 
    1180              :     @throws std::bad_alloc if allocation fails.
    1181              :     @throws Any exception thrown by `T`'s constructor.
    1182              : */
    1183              : template<class T, class... Args>
    1184              : typename std::enable_if<
    1185              :     !std::is_array<T>::value,
    1186              :     neunique_ptr<T>>::type
    1187            2 : make_neunique(Args&&... args)
    1188              : {
    1189            2 :     return neunique_ptr<T>(new T(std::forward<Args>(args)...));
    1190              : }
    1191              : 
    1192              : /** Create a neunique_ptr for an array.
    1193              : 
    1194              :     Allocates an array of `n` value-initialized elements
    1195              :     using `new[]`. No control block is allocated.
    1196              : 
    1197              :     @param n Number of elements.
    1198              : 
    1199              :     @return A `neunique_ptr<T[]>` owning the new array.
    1200              : 
    1201              :     @throws std::bad_alloc if allocation fails.
    1202              : */
    1203              : template<class T>
    1204              : typename std::enable_if<
    1205              :     std::is_array<T>::value && std::extent<T>::value == 0,
    1206              :     neunique_ptr<T>>::type
    1207            1 : make_neunique(std::size_t n)
    1208              : {
    1209              :     using U = typename std::remove_extent<T>::type;
    1210            6 :     return neunique_ptr<T>(new U[n]());
    1211              : }
    1212              : 
    1213              : template<class T, class... Args>
    1214              : typename std::enable_if<
    1215              :     std::extent<T>::value != 0>::type
    1216              : make_neunique(Args&&...) = delete;
    1217              : 
    1218              : //----------------------------------------------------------
    1219              : 
    1220              : /** Create a neunique_ptr using a custom allocator.
    1221              : 
    1222              :     Allocates and constructs an object of type `T` using
    1223              :     the specified allocator. The object and control block
    1224              :     are allocated together for efficiency.
    1225              : 
    1226              :     @param a Allocator to use.
    1227              :     @param args Arguments forwarded to `T`'s constructor.
    1228              : 
    1229              :     @return A `neunique_ptr<T>` owning the new object.
    1230              : 
    1231              :     @throws Any exception thrown by allocation or construction.
    1232              : */
    1233              : template<class T, class A, class... Args>
    1234              : typename std::enable_if<
    1235              :     !std::is_array<T>::value,
    1236              :     neunique_ptr<T>>::type
    1237            2 : allocate_neunique(A const& a, Args&&... args)
    1238              : {
    1239              :     using cb_type = detail::control_block_embedded<T, A>;
    1240              :     using alloc_type = typename cb_type::alloc_type;
    1241              :     using alloc_traits = std::allocator_traits<alloc_type>;
    1242              : 
    1243            1 :     alloc_type alloc(a);
    1244            2 :     cb_type* cb = alloc_traits::allocate(alloc, 1);
    1245            2 :     auto guard = detail::make_scope_guard(
    1246            0 :         [&]{ alloc_traits::deallocate(alloc, cb, 1); });
    1247            2 :     ::new(static_cast<void*>(cb)) cb_type(
    1248              :         a, std::forward<Args>(args)...);
    1249            2 :     guard.release();
    1250              : 
    1251            2 :     neunique_ptr<T> result;
    1252            2 :     result.ptr_ = cb->get();
    1253            2 :     result.cb_ = cb;
    1254            4 :     return result;
    1255            2 : }
    1256              : 
    1257              : /** Create a neunique_ptr for an array using a custom allocator.
    1258              : 
    1259              :     Allocates an array of `n` value-initialized elements
    1260              :     using the specified allocator.
    1261              : 
    1262              :     @param a Allocator to use.
    1263              :     @param n Number of elements.
    1264              : 
    1265              :     @return A `neunique_ptr<T[]>` owning the new array.
    1266              : 
    1267              :     @throws Any exception thrown by allocation or construction.
    1268              : */
    1269              : template<class T, class A>
    1270              : typename std::enable_if<
    1271              :     std::is_array<T>::value && std::extent<T>::value == 0,
    1272              :     neunique_ptr<T>>::type
    1273            1 : allocate_neunique(A const& a, std::size_t n)
    1274              : {
    1275              :     using U = typename std::remove_extent<T>::type;
    1276              :     using cb_type = detail::control_block_array<U, A>;
    1277              :     using alloc_type = typename cb_type::alloc_type;
    1278              :     using alloc_traits = std::allocator_traits<alloc_type>;
    1279              : 
    1280            1 :     alloc_type alloc(a);
    1281            1 :     std::size_t units = (cb_type::storage_size(n) +
    1282            1 :         sizeof(cb_type) - 1) / sizeof(cb_type);
    1283            1 :     cb_type* cb = alloc_traits::allocate(alloc, units);
    1284              : 
    1285            1 :     auto guard = detail::make_scope_guard(
    1286            0 :         [&]{ alloc_traits::deallocate(alloc, cb, units); });
    1287              : 
    1288            1 :     ::new(static_cast<void*>(cb)) cb_type(a);
    1289              : 
    1290            1 :     U* arr = cb->get();
    1291            4 :     for(std::size_t i = 0; i < n; ++i)
    1292              :     {
    1293            3 :         ::new(static_cast<void*>(arr + i)) U();
    1294            3 :         ++cb->size;
    1295              :     }
    1296            1 :     guard.release();
    1297              : 
    1298            1 :     neunique_ptr<T> result;
    1299            1 :     result.ptr_ = arr;
    1300            1 :     result.cb_ = cb;
    1301            2 :     return result;
    1302            1 : }
    1303              : 
    1304              : //----------------------------------------------------------
    1305              : 
    1306              : /** Swap two pointers.
    1307              : 
    1308              :     @param a First pointer.
    1309              :     @param b Second pointer.
    1310              : */
    1311              : template<class T>
    1312            1 : void swap(neunique_ptr<T>& a, neunique_ptr<T>& b) noexcept
    1313              : {
    1314            1 :     a.swap(b);
    1315            1 : }
    1316              : 
    1317              : //----------------------------------------------------------
    1318              : //
    1319              : // Comparison operators
    1320              : //
    1321              : //----------------------------------------------------------
    1322              : 
    1323              : /** Compare for equality.
    1324              : 
    1325              :     @return `true` if both store the same pointer.
    1326              : */
    1327              : template<class T, class U>
    1328            1 : bool operator==(
    1329              :     neunique_ptr<T> const& a,
    1330              :     neunique_ptr<U> const& b) noexcept
    1331              : {
    1332            1 :     return a.get() == b.get();
    1333              : }
    1334              : 
    1335              : /** Compare with nullptr.
    1336              : 
    1337              :     @return `true` if `a` is empty.
    1338              : */
    1339              : template<class T>
    1340            1 : bool operator==(
    1341              :     neunique_ptr<T> const& a,
    1342              :     std::nullptr_t) noexcept
    1343              : {
    1344            1 :     return !a;
    1345              : }
    1346              : 
    1347              : /** Compare with nullptr.
    1348              : 
    1349              :     @return `true` if `a` is empty.
    1350              : */
    1351              : template<class T>
    1352            1 : bool operator==(
    1353              :     std::nullptr_t,
    1354              :     neunique_ptr<T> const& a) noexcept
    1355              : {
    1356            1 :     return !a;
    1357              : }
    1358              : 
    1359              : /** Compare for inequality.
    1360              : 
    1361              :     @return `true` if pointers differ.
    1362              : */
    1363              : template<class T, class U>
    1364            1 : bool operator!=(
    1365              :     neunique_ptr<T> const& a,
    1366              :     neunique_ptr<U> const& b) noexcept
    1367              : {
    1368            1 :     return a.get() != b.get();
    1369              : }
    1370              : 
    1371              : /** Compare with nullptr.
    1372              : 
    1373              :     @return `true` if `a` is non-empty.
    1374              : */
    1375              : template<class T>
    1376            1 : bool operator!=(
    1377              :     neunique_ptr<T> const& a,
    1378              :     std::nullptr_t) noexcept
    1379              : {
    1380            1 :     return static_cast<bool>(a);
    1381              : }
    1382              : 
    1383              : /** Compare with nullptr.
    1384              : 
    1385              :     @return `true` if `a` is non-empty.
    1386              : */
    1387              : template<class T>
    1388            1 : bool operator!=(
    1389              :     std::nullptr_t,
    1390              :     neunique_ptr<T> const& a) noexcept
    1391              : {
    1392            1 :     return static_cast<bool>(a);
    1393              : }
    1394              : 
    1395              : /** Less-than comparison.
    1396              : 
    1397              :     @return `true` if `a.get() < b.get()`.
    1398              : */
    1399              : template<class T, class U>
    1400            4 : bool operator<(
    1401              :     neunique_ptr<T> const& a,
    1402              :     neunique_ptr<U> const& b) noexcept
    1403              : {
    1404              :     using V = typename std::common_type<
    1405              :         typename neunique_ptr<T>::pointer,
    1406              :         typename neunique_ptr<U>::pointer>::type;
    1407            4 :     return std::less<V>()(a.get(), b.get());
    1408              : }
    1409              : 
    1410              : /** Less-than comparison with nullptr.
    1411              : 
    1412              :     @return `true` if `a.get() < nullptr`.
    1413              : */
    1414              : template<class T>
    1415              : bool operator<(
    1416              :     neunique_ptr<T> const& a,
    1417              :     std::nullptr_t) noexcept
    1418              : {
    1419              :     return std::less<typename neunique_ptr<T>::pointer>()(
    1420              :         a.get(), nullptr);
    1421              : }
    1422              : 
    1423              : /** Less-than comparison with nullptr.
    1424              : 
    1425              :     @return `true` if `nullptr < a.get()`.
    1426              : */
    1427              : template<class T>
    1428              : bool operator<(
    1429              :     std::nullptr_t,
    1430              :     neunique_ptr<T> const& a) noexcept
    1431              : {
    1432              :     return std::less<typename neunique_ptr<T>::pointer>()(
    1433              :         nullptr, a.get());
    1434              : }
    1435              : 
    1436              : /** Less-than-or-equal comparison.
    1437              : 
    1438              :     @return `true` if `a.get() <= b.get()`.
    1439              : */
    1440              : template<class T, class U>
    1441            1 : bool operator<=(
    1442              :     neunique_ptr<T> const& a,
    1443              :     neunique_ptr<U> const& b) noexcept
    1444              : {
    1445            1 :     return !(b < a);
    1446              : }
    1447              : 
    1448              : /** Less-than-or-equal comparison with nullptr.
    1449              : 
    1450              :     @return `true` if `a.get() <= nullptr`.
    1451              : */
    1452              : template<class T>
    1453              : bool operator<=(
    1454              :     neunique_ptr<T> const& a,
    1455              :     std::nullptr_t) noexcept
    1456              : {
    1457              :     return !(nullptr < a);
    1458              : }
    1459              : 
    1460              : /** Less-than-or-equal comparison with nullptr.
    1461              : 
    1462              :     @return `true` if `nullptr <= a.get()`.
    1463              : */
    1464              : template<class T>
    1465              : bool operator<=(
    1466              :     std::nullptr_t,
    1467              :     neunique_ptr<T> const& a) noexcept
    1468              : {
    1469              :     return !(a < nullptr);
    1470              : }
    1471              : 
    1472              : /** Greater-than comparison.
    1473              : 
    1474              :     @return `true` if `a.get() > b.get()`.
    1475              : */
    1476              : template<class T, class U>
    1477            1 : bool operator>(
    1478              :     neunique_ptr<T> const& a,
    1479              :     neunique_ptr<U> const& b) noexcept
    1480              : {
    1481            1 :     return b < a;
    1482              : }
    1483              : 
    1484              : /** Greater-than comparison with nullptr.
    1485              : 
    1486              :     @return `true` if `a.get() > nullptr`.
    1487              : */
    1488              : template<class T>
    1489              : bool operator>(
    1490              :     neunique_ptr<T> const& a,
    1491              :     std::nullptr_t) noexcept
    1492              : {
    1493              :     return nullptr < a;
    1494              : }
    1495              : 
    1496              : /** Greater-than comparison with nullptr.
    1497              : 
    1498              :     @return `true` if `nullptr > a.get()`.
    1499              : */
    1500              : template<class T>
    1501              : bool operator>(
    1502              :     std::nullptr_t,
    1503              :     neunique_ptr<T> const& a) noexcept
    1504              : {
    1505              :     return a < nullptr;
    1506              : }
    1507              : 
    1508              : /** Greater-than-or-equal comparison.
    1509              : 
    1510              :     @return `true` if `a.get() >= b.get()`.
    1511              : */
    1512              : template<class T, class U>
    1513            1 : bool operator>=(
    1514              :     neunique_ptr<T> const& a,
    1515              :     neunique_ptr<U> const& b) noexcept
    1516              : {
    1517            1 :     return !(a < b);
    1518              : }
    1519              : 
    1520              : /** Greater-than-or-equal comparison with nullptr.
    1521              : 
    1522              :     @return `true` if `a.get() >= nullptr`.
    1523              : */
    1524              : template<class T>
    1525              : bool operator>=(
    1526              :     neunique_ptr<T> const& a,
    1527              :     std::nullptr_t) noexcept
    1528              : {
    1529              :     return !(a < nullptr);
    1530              : }
    1531              : 
    1532              : /** Greater-than-or-equal comparison with nullptr.
    1533              : 
    1534              :     @return `true` if `nullptr >= a.get()`.
    1535              : */
    1536              : template<class T>
    1537              : bool operator>=(
    1538              :     std::nullptr_t,
    1539              :     neunique_ptr<T> const& a) noexcept
    1540              : {
    1541              :     return !(nullptr < a);
    1542              : }
    1543              : 
    1544              : } // capy
    1545              : } // boost
    1546              : 
    1547              : //----------------------------------------------------------
    1548              : //
    1549              : // Hash support
    1550              : //
    1551              : //----------------------------------------------------------
    1552              : 
    1553              : namespace std {
    1554              : 
    1555              : /** Hash support for neunique_ptr.
    1556              : 
    1557              :     Allows `neunique_ptr` to be used as a key in
    1558              :     unordered containers.
    1559              : */
    1560              : template<class T>
    1561              : struct hash<::boost::capy::neunique_ptr<T>>
    1562              : {
    1563              :     /** Return hash value for a neunique_ptr.
    1564              : 
    1565              :         @param p Pointer to hash.
    1566              : 
    1567              :         @return Hash of the stored pointer.
    1568              :     */
    1569            3 :     std::size_t operator()(
    1570              :         ::boost::capy::neunique_ptr<T> const& p) const noexcept
    1571              :     {
    1572            3 :         return std::hash<typename boost::capy::neunique_ptr<T>::pointer>()(p.get());
    1573              :     }
    1574              : };
    1575              : 
    1576              : } // std
    1577              : 
    1578              : #endif
        

Generated by: LCOV version 2.1