mirror of
https://github.com/microsoft/GSL.git
synced 2024-11-03 17:56:43 -05:00
not_null cleanups and improvements: (#449)
* constexpr all the things. * remove operator=(const T&) * it leaves *this in an invalid state if ensure_invariant fails * implicitly converting the T to not_null and then assigning is in every way superior. * simplify conversion from not_null<U> with constructor delegation. * remove the converting assignment operator; again let the conversion constructor and self-assignment operator do the work. * Cover the remaining pointer arithmetic operations as Wakely suggests in issue #447. * Cleanup not_null conversions from null pointer constants: * replace constructor that accepts T with constructor template that accepts U convertible to T * remove deleted constructor that accepts int * Attempts to initialize with nullptr, 0, 0L, 0LL, etc. all unambiguously select the deleted nullptr_t constructor.
This commit is contained in:
parent
6367b42ac5
commit
e3fecbd1c5
@ -62,81 +62,63 @@ using owner = T;
|
|||||||
// Has zero size overhead over T.
|
// Has zero size overhead over T.
|
||||||
//
|
//
|
||||||
// If T is a pointer (i.e. T == U*) then
|
// If T is a pointer (i.e. T == U*) then
|
||||||
// - allow construction from U* or U&
|
// - allow construction from U*
|
||||||
// - disallow construction from nullptr_t
|
// - disallow construction from nullptr_t
|
||||||
// - disallow default construction
|
// - disallow default construction
|
||||||
// - ensure construction from U* fails with nullptr
|
// - ensure construction from null U* fails
|
||||||
// - allow implicit conversion to U*
|
// - allow implicit conversion to U*
|
||||||
//
|
//
|
||||||
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.");
|
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
|
||||||
|
|
||||||
public:
|
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||||
not_null(T t) : ptr_(t) { ensure_invariant(); }
|
constexpr not_null(U&& u) : ptr_(std::forward<U>(u)) { Expects(ptr_ != nullptr); }
|
||||||
not_null& operator=(const T& t)
|
|
||||||
{
|
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||||
ptr_ = t;
|
constexpr not_null(const not_null<U>& other) : not_null(other.get()) {}
|
||||||
ensure_invariant();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
|
constexpr T get() const
|
||||||
not_null(const not_null<U>& other)
|
|
||||||
{
|
{
|
||||||
*this = other;
|
Ensures(ptr_ != nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
|
|
||||||
not_null& operator=(const not_null<U>& other)
|
|
||||||
{
|
|
||||||
ptr_ = other.get();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevents compilation when someone attempts to assign a nullptr
|
|
||||||
not_null(std::nullptr_t) = delete;
|
|
||||||
not_null(int) = delete;
|
|
||||||
not_null<T>& operator=(std::nullptr_t) = delete;
|
|
||||||
not_null<T>& operator=(int) = delete;
|
|
||||||
|
|
||||||
T get() const
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
__assume(ptr_ != nullptr);
|
|
||||||
#endif
|
|
||||||
return ptr_;
|
return ptr_;
|
||||||
} // the assume() should help the optimizer
|
}
|
||||||
|
|
||||||
operator T() const { return get(); }
|
constexpr operator T() const { return get(); }
|
||||||
T operator->() const { return get(); }
|
constexpr T operator->() const { return get(); }
|
||||||
|
|
||||||
bool operator==(const T& rhs) const { return ptr_ == rhs; }
|
// prevents compilation when someone attempts to assign a null pointer constant
|
||||||
bool operator!=(const T& rhs) const { return !(*this == rhs); }
|
not_null(std::nullptr_t) = delete;
|
||||||
private:
|
not_null& operator=(std::nullptr_t) = delete;
|
||||||
T ptr_;
|
|
||||||
|
|
||||||
// we assume that the compiler can hoist/prove away most of the checks inlined from this
|
|
||||||
// function
|
|
||||||
// if not, we could make them optional via conditional compilation
|
|
||||||
void ensure_invariant() const { Expects(ptr_ != nullptr); }
|
|
||||||
|
|
||||||
// unwanted operators...pointers only point to single objects!
|
// unwanted operators...pointers only point to single objects!
|
||||||
// TODO ensure all arithmetic ops on this type are unavailable
|
not_null& operator++() = delete;
|
||||||
not_null<T>& operator++() = delete;
|
not_null& operator--() = delete;
|
||||||
not_null<T>& operator--() = delete;
|
not_null operator++(int) = delete;
|
||||||
not_null<T> operator++(int) = delete;
|
not_null operator--(int) = delete;
|
||||||
not_null<T> operator--(int) = delete;
|
not_null& operator+=(std::ptrdiff_t) = delete;
|
||||||
not_null<T>& operator+(size_t) = delete;
|
not_null& operator-=(std::ptrdiff_t) = delete;
|
||||||
not_null<T>& operator+=(size_t) = delete;
|
void operator[](std::ptrdiff_t) const = delete;
|
||||||
not_null<T>& operator-(size_t) = delete;
|
|
||||||
not_null<T>& operator-=(size_t) = delete;
|
private:
|
||||||
|
T ptr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// more unwanted operators
|
||||||
|
template <class T, class U>
|
||||||
|
std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
|
||||||
|
template <class T>
|
||||||
|
not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
|
||||||
|
template <class T>
|
||||||
|
not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
|
||||||
|
template <class T>
|
||||||
|
not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
|
||||||
|
|
||||||
} // namespace gsl
|
} // namespace gsl
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
|
Loading…
Reference in New Issue
Block a user