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

Generated by: LCOV version 2.3