diff --git a/include/gsl/span b/include/gsl/span index 3e5a053..b3f4f83 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -46,8 +46,8 @@ #define constexpr /*constexpr*/ #define GSL_USE_STATIC_CONSTEXPR_WORKAROUND -#endif // _MSC_VER < 1910 -#endif // _MSC_VER +#endif // _MSC_VER < 1910 +#endif // _MSC_VER // See if we have enough C++17 power to use a static constexpr data member // without needing an out-of-line definition @@ -142,15 +142,13 @@ namespace details constexpr span_iterator(const Span* span, typename Span::index_type idx) noexcept : span_(span), index_(idx) - { - } + {} friend span_iterator; template * = nullptr> constexpr span_iterator(const span_iterator& other) noexcept : span_iterator(other.span_, other.index_) - { - } + {} GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr reference operator*() const @@ -332,8 +330,7 @@ namespace details template explicit constexpr extent_type(extent_type ext) : size_(ext.size()) - { - } + {} explicit constexpr extent_type(index_type size) : size_(size) { Expects(size >= 0); } @@ -383,35 +380,30 @@ public: // since "std::enable_if_t" is ill-formed when Extent is greater than 0. class = std::enable_if_t<(Dependent || Extent <= 0)>> constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) - { - } + {} constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {} constexpr span(pointer firstElem, pointer lastElem) : storage_(firstElem, std::distance(firstElem, lastElem)) - { - } + {} template constexpr span(element_type (&arr)[N]) noexcept : storage_(KnownNotNull{&arr[0]}, details::extent_type()) - { - } + {} template > // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug constexpr span(std::array& arr) noexcept : storage_(&arr[0], details::extent_type()) - { - } + {} template // GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute // TODO: parser bug constexpr span(const std::array, N>& arr) noexcept : storage_(&arr[0], details::extent_type()) - { - } + {} // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the requirement // on Container to be a contiguous sequence container. @@ -422,8 +414,7 @@ public: std::is_convertible().data())>::value>> constexpr span(Container& cont) : span(cont.data(), narrow(cont.size())) - { - } + {} template ().data())>::value>> constexpr span(const Container& cont) : span(cont.data(), narrow(cont.size())) - { - } + {} constexpr span(const span& other) noexcept = default; @@ -444,8 +434,7 @@ public: details::is_allowed_element_type_conversion::value>> constexpr span(const span& other) : storage_(other.data(), details::extent_type(other.size())) - { - } + {} ~span() noexcept = default; constexpr span& operator=(const span& other) noexcept = default; @@ -506,7 +495,7 @@ public: GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute constexpr reference operator[](index_type idx) const { - Expects(idx >= 0 && idx < storage_.size()); + Expects(CheckRange(idx, storage_.size())); return data()[idx]; } @@ -536,7 +525,7 @@ public: #ifdef _MSC_VER // Tell MSVC how to unwrap spans in range-based-for constexpr pointer _Unchecked_begin() const noexcept { return data(); } - constexpr pointer _Unchecked_end() const noexcept + constexpr pointer _Unchecked_end() const noexcept { GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute return data() + size(); @@ -544,6 +533,29 @@ public: #endif // _MSC_VER private: + static bool CheckRange(index_type idx, index_type size) + { + // Optimization: + // + // idx >= 0 && idx < size + // => + // static_cast(idx) < static_cast(size) + // + // because size >=0 by span construction, and negative idx will + // wrap around to a value always greater than size when casted. + + // check if we have enough space to wrap around + if (narrow_cast(std::numeric_limits::max()) < + narrow_cast(std::numeric_limits::max())) + { + return narrow_cast(idx) < narrow_cast(size); + } + else + { + return idx >= 0 && idx < size; + } + } + // Needed to remove unnecessary null check in subspans struct KnownNotNull {