patterncppMinor
Yet another shared pointer
Viewed 0 times
yetpointeranothershared
Problem
What might be wrong with this shared pointer? One good point of it might be, that it should handle array types correctly by default (e.g.
```
#pragma once
#ifndef LIGHTPTR_HPP
# define LIGHTPTR_HPP
#include
#include
#include
#include
#include
namespace detail
{
using counter_type = ::std::size_t;
using atomic_type = ::std::atomic;
template
using deleter_type = void ()(T);
template
struct ref_type
{
using type = U&;
};
template <>
struct ref_type
{
using type = void;
};
template
inline void dec_ref(atomic_type* const counter_ptr,
T* const ptr, deleter_type const deleter)
{
if (counter_ptr && (counter_type(1) ==
counter_ptr->fetch_sub(counter_type(1), ::std::memory_order_relaxed)))
{
delete counter_ptr;
deleter(ptr);
}
// else do nothing
}
inline void inc_ref(atomic_type* const counter_ptr)
{
assert(counter_ptr);
counter_ptr->fetch_add(counter_type(1), ::std::memory_order_relaxed);
}
}
template
struct light_ptr
{
template
struct deletion_type
{
using type = V;
};
template
struct deletion_type
{
using type = V[];
};
template
struct deletion_type
{
using type = V[];
};
template
struct remove_array
{
using type = U;
};
template
struct remove_array
{
using type = U;
};
template
struct remove_array
{
using type = U;
};
using element_type = typename remove_array::type;
using deleter_type = ::detail::deleter_type;
light_ptr() = default;
template
explicit light_ptr(U* const p,
deleter_type const d = default_deleter)
{
reset(p, d);
}
~light_ptr() { ::detail::dec_ref(counter_ptr_, p
light_ptr(new int[10]).std::shared_ptr has the advantage of being more portable. For example, emscripten provides an implementation, but light_ptr won't compile, still, it can be ported to emscripten easily. The native implementation may also be more optimized.```
#pragma once
#ifndef LIGHTPTR_HPP
# define LIGHTPTR_HPP
#include
#include
#include
#include
#include
namespace detail
{
using counter_type = ::std::size_t;
using atomic_type = ::std::atomic;
template
using deleter_type = void ()(T);
template
struct ref_type
{
using type = U&;
};
template <>
struct ref_type
{
using type = void;
};
template
inline void dec_ref(atomic_type* const counter_ptr,
T* const ptr, deleter_type const deleter)
{
if (counter_ptr && (counter_type(1) ==
counter_ptr->fetch_sub(counter_type(1), ::std::memory_order_relaxed)))
{
delete counter_ptr;
deleter(ptr);
}
// else do nothing
}
inline void inc_ref(atomic_type* const counter_ptr)
{
assert(counter_ptr);
counter_ptr->fetch_add(counter_type(1), ::std::memory_order_relaxed);
}
}
template
struct light_ptr
{
template
struct deletion_type
{
using type = V;
};
template
struct deletion_type
{
using type = V[];
};
template
struct deletion_type
{
using type = V[];
};
template
struct remove_array
{
using type = U;
};
template
struct remove_array
{
using type = U;
};
template
struct remove_array
{
using type = U;
};
using element_type = typename remove_array::type;
using deleter_type = ::detail::deleter_type;
light_ptr() = default;
template
explicit light_ptr(U* const p,
deleter_type const d = default_deleter)
{
reset(p, d);
}
~light_ptr() { ::detail::dec_ref(counter_ptr_, p
Solution
The move assignment operator is broken:
You over write the current members with the values from the rhs, but do not decrement the reference counter before overwriting. It might be easier to swap
Don't include types that are not being used:
Casting to void*. Once this is done the only valid operation is to cast back to the original type. Annything else is undefined.
In this case
light_ptr& operator=(light_ptr&& rhs) noexceptYou over write the current members with the values from the rhs, but do not decrement the reference counter before overwriting. It might be easier to swap
*this with rhs. Don't include types that are not being used:
template
struct deletion_type
{
using type = V[];
};
template
struct remove_array
{
using type = U;
};Casting to void*. Once this is done the only valid operation is to cast back to the original type. Annything else is undefined.
return static_cast(static_cast(ptr_));In this case
element_type is the same as T. But the code is written in such a way that any changes by a maintainer can easily result in this being not true. Also I don;t actually see the need for a cast.Code Snippets
light_ptr& operator=(light_ptr&& rhs) noexcepttemplate <typename U, typename V, ::std::size_t N>
struct deletion_type<U[N], V>
{
using type = V[];
};
template <typename U, ::std::size_t N>
struct remove_array<U[N]>
{
using type = U;
};return static_cast<T*>(static_cast<void*>(ptr_));Context
StackExchange Code Review Q#30398, answer score: 4
Revisions (0)
No revisions yet.