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.
|
||||
//
|
||||
// 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 default construction
|
||||
// - ensure construction from U* fails with nullptr
|
||||
// - ensure construction from null U* fails
|
||||
// - allow implicit conversion to U*
|
||||
//
|
||||
template <class T>
|
||||
class not_null
|
||||
{
|
||||
public:
|
||||
static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
|
||||
|
||||
public:
|
||||
not_null(T t) : ptr_(t) { ensure_invariant(); }
|
||||
not_null& operator=(const T& t)
|
||||
{
|
||||
ptr_ = t;
|
||||
ensure_invariant();
|
||||
return *this;
|
||||
}
|
||||
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr not_null(U&& u) : ptr_(std::forward<U>(u)) { Expects(ptr_ != nullptr); }
|
||||
|
||||
template <typename U, typename Dummy = std::enable_if_t<std::is_convertible<U, T>::value>>
|
||||
constexpr not_null(const not_null<U>& other) : not_null(other.get()) {}
|
||||
|
||||
not_null(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>>
|
||||
not_null(const not_null<U>& other)
|
||||
constexpr T get() const
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
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
|
||||
Ensures(ptr_ != nullptr);
|
||||
return ptr_;
|
||||
} // the assume() should help the optimizer
|
||||
}
|
||||
|
||||
operator T() const { return get(); }
|
||||
T operator->() const { return get(); }
|
||||
constexpr operator T() const { return get(); }
|
||||
constexpr T operator->() const { return get(); }
|
||||
|
||||
bool operator==(const T& rhs) const { return ptr_ == rhs; }
|
||||
bool operator!=(const T& rhs) const { return !(*this == rhs); }
|
||||
private:
|
||||
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); }
|
||||
// prevents compilation when someone attempts to assign a null pointer constant
|
||||
not_null(std::nullptr_t) = delete;
|
||||
not_null& operator=(std::nullptr_t) = delete;
|
||||
|
||||
// unwanted operators...pointers only point to single objects!
|
||||
// TODO ensure all arithmetic ops on this type are unavailable
|
||||
not_null<T>& operator++() = delete;
|
||||
not_null<T>& operator--() = delete;
|
||||
not_null<T> operator++(int) = delete;
|
||||
not_null<T> operator--(int) = delete;
|
||||
not_null<T>& operator+(size_t) = delete;
|
||||
not_null<T>& operator+=(size_t) = delete;
|
||||
not_null<T>& operator-(size_t) = delete;
|
||||
not_null<T>& operator-=(size_t) = delete;
|
||||
not_null& operator++() = delete;
|
||||
not_null& operator--() = delete;
|
||||
not_null operator++(int) = delete;
|
||||
not_null operator--(int) = delete;
|
||||
not_null& operator+=(std::ptrdiff_t) = delete;
|
||||
not_null& operator-=(std::ptrdiff_t) = delete;
|
||||
void operator[](std::ptrdiff_t) const = 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 std
|
||||
|
Loading…
Reference in New Issue
Block a user