not_null no longer requires a copyable pointer type, now supporting std::unique_ptr

This commit is contained in:
Zac Hansen 2018-04-12 23:24:47 -07:00
parent 7757e7ec9f
commit 63cf89f340
2 changed files with 51 additions and 6 deletions

View File

@ -68,10 +68,9 @@ using owner = T;
template <class T> template <class T>
class not_null class not_null
{ {
public:
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>> public:
template <typename U, typename = typename std::enable_if<std::is_constructible<T, U>::value>::type>
constexpr not_null(U&& u) : ptr_(std::forward<U>(u)) constexpr not_null(U&& u) : ptr_(std::forward<U>(u))
{ {
Expects(ptr_ != nullptr); Expects(ptr_ != nullptr);
@ -86,7 +85,8 @@ public:
not_null(const not_null& other) = default; not_null(const not_null& other) = default;
not_null& operator=(const not_null& other) = default; not_null& operator=(const not_null& other) = default;
constexpr T get() const
constexpr T const & get() const
{ {
/* /*
not_null constructors and assignment operators always verify ptr_ is not_null constructors and assignment operators always verify ptr_ is
@ -97,8 +97,8 @@ public:
return ptr_; return ptr_;
} }
constexpr operator T() const { return get(); } constexpr operator T const & () const { return get(); }
constexpr T operator->() const { return get(); } constexpr T const & operator->() const { return get(); }
constexpr decltype(auto) operator*() const { return *get(); } constexpr decltype(auto) operator*() const { return *get(); }
// prevents compilation when someone attempts to assign a null pointer constant // prevents compilation when someone attempts to assign a null pointer constant

View File

@ -56,6 +56,7 @@ struct CustomPtr
{ {
CustomPtr(T* p) : p_(p) {} CustomPtr(T* p) : p_(p) {}
operator T*() { return p_; } operator T*() { return p_; }
operator T*() const { return p_; }
bool operator!=(std::nullptr_t) const { return p_ != nullptr; } bool operator!=(std::nullptr_t) const { return p_ != nullptr; }
T* p_ = nullptr; T* p_ = nullptr;
}; };
@ -329,3 +330,47 @@ TEST_CASE("TestNotNullCustomPtrComparison")
} }
static_assert(std::is_nothrow_move_constructible<not_null<void *>>::value, "not_null must be no-throw move constructible"); static_assert(std::is_nothrow_move_constructible<not_null<void *>>::value, "not_null must be no-throw move constructible");
struct UniquePointerTestStruct {
int i = 0;
};
TEST_CASE("TestNotNullUniquePtrComparison") {
{
using NotNull1 = not_null<std::unique_ptr<int>>;
// values are the same
CHECK((*NotNull1(std::make_unique<int>(42)) == *NotNull1(std::make_unique<int>(42))));
}
{
using NotNull1 = not_null<std::unique_ptr<UniquePointerTestStruct>>;
// values are the same
CHECK((NotNull1(std::make_unique<UniquePointerTestStruct>())->i == NotNull1(std::make_unique<UniquePointerTestStruct>())->i));
}
}
template<typename T>
struct UncopyableUnmovablePointerLikeType {
private:
T * t;
public:
UncopyableUnmovablePointerLikeType(T * pointer) : t(pointer) {}
UncopyableUnmovablePointerLikeType(const UncopyableUnmovablePointerLikeType&) = delete;
UncopyableUnmovablePointerLikeType(UncopyableUnmovablePointerLikeType &&) = delete;
operator T*() { return t; }
operator T*() const { return t; }
};
// this test case makes sure not_null works on move-only types like std::unique_ptr as well as verifying
// that no copy penalty is being paid for types with expensive copy constructors like std::shared_ptr
TEST_CASE("ConfirmCopyableAndMoveablePointerTypeNotRequired") {
int i = 5;
gsl::not_null<UncopyableUnmovablePointerLikeType<int>> fixed_in_place(&i);
CHECK(*fixed_in_place == 5);
}