Home | History | Annotate | Download | only in gsl
      1 ///////////////////////////////////////////////////////////////////////////////
      2 //
      3 // Copyright (c) 2015 Microsoft Corporation. All rights reserved.
      4 //
      5 // This code is licensed under the MIT License (MIT).
      6 //
      7 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      8 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      9 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     10 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     11 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     12 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     13 // THE SOFTWARE.
     14 //
     15 ///////////////////////////////////////////////////////////////////////////////
     16 
     17 #pragma once
     18 
     19 #ifndef GSL_MULTI_SPAN_H
     20 #define GSL_MULTI_SPAN_H
     21 
     22 #include "gsl_assert"
     23 #include "gsl_byte"
     24 #include "gsl_util"
     25 #include <algorithm>
     26 #include <array>
     27 #include <cassert>
     28 #include <cstddef>
     29 #include <cstdint>
     30 #include <functional>
     31 #include <iterator>
     32 #include <limits>
     33 #include <new>
     34 #include <numeric>
     35 #include <stdexcept>
     36 #include <type_traits>
     37 #include <utility>
     38 
     39 #ifdef _MSC_VER
     40 
     41 // turn off some warnings that are noisy about our Expects statements
     42 #pragma warning(push)
     43 #pragma warning(disable : 4127) // conditional expression is constant
     44 
     45 // No MSVC does constexpr fully yet
     46 #pragma push_macro("constexpr")
     47 #define constexpr /*constexpr*/
     48 
     49 // VS 2013 workarounds
     50 #if _MSC_VER <= 1800
     51 
     52 #define GSL_MSVC_HAS_VARIADIC_CTOR_BUG
     53 #define GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT
     54 
     55 // noexcept is not understood
     56 #ifndef GSL_THROW_ON_CONTRACT_VIOLATION
     57 #pragma push_macro("noexcept")
     58 #define noexcept /*noexcept*/
     59 #endif
     60 
     61 // turn off some misguided warnings
     62 #pragma warning(push)
     63 #pragma warning(disable : 4351) // warns about newly introduced aggregate initializer behavior
     64 #pragma warning(disable : 4512) // warns that assignment op could not be generated
     65 
     66 #endif // _MSC_VER <= 1800
     67 
     68 #endif // _MSC_VER
     69 
     70 #ifdef GSL_THROW_ON_CONTRACT_VIOLATION
     71 
     72 #ifdef _MSC_VER
     73 #pragma push_macro("noexcept")
     74 #endif
     75 
     76 #define noexcept /*noexcept*/
     77 
     78 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
     79 
     80 namespace gsl
     81 {
     82 
     83 /*
     84 ** begin definitions of index and bounds
     85 */
     86 namespace details
     87 {
     88     template <typename SizeType>
     89     struct SizeTypeTraits
     90     {
     91         static const SizeType max_value = std::numeric_limits<SizeType>::max();
     92     };
     93 
     94     template <typename... Ts>
     95     class are_integral : public std::integral_constant<bool, true>
     96     {
     97     };
     98 
     99     template <typename T, typename... Ts>
    100     class are_integral<T, Ts...>
    101         : public std::integral_constant<bool,
    102                                         std::is_integral<T>::value && are_integral<Ts...>::value>
    103     {
    104     };
    105 }
    106 
    107 template <size_t Rank>
    108 class index final
    109 {
    110     static_assert(Rank > 0, "Rank must be greater than 0!");
    111 
    112     template <size_t OtherRank>
    113     friend class index;
    114 
    115 public:
    116     static const size_t rank = Rank;
    117     using value_type = std::ptrdiff_t;
    118     using size_type = value_type;
    119     using reference = std::add_lvalue_reference_t<value_type>;
    120     using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
    121 
    122     constexpr index() noexcept {}
    123 
    124     constexpr index(const value_type (&values)[Rank]) noexcept
    125     {
    126         std::copy(values, values + Rank, elems);
    127     }
    128 
    129 #ifdef GSL_MSVC_HAS_VARIADIC_CTOR_BUG
    130     template <
    131         typename T, typename... Ts,
    132         typename = std::enable_if_t<((sizeof...(Ts) + 1) == Rank) && std::is_integral<T>::value &&
    133                                     details::are_integral<Ts...>::value>>
    134     constexpr index(T t, Ts... ds)
    135         : index({narrow_cast<value_type>(t), narrow_cast<value_type>(ds)...})
    136     {
    137     }
    138 #else
    139     template <typename... Ts, typename = std::enable_if_t<(sizeof...(Ts) == Rank) &&
    140                                                           details::are_integral<Ts...>::value>>
    141     constexpr index(Ts... ds) noexcept : elems{narrow_cast<value_type>(ds)...}
    142     {
    143     }
    144 #endif
    145 
    146     constexpr index(const index& other) noexcept = default;
    147 
    148     constexpr index& operator=(const index& rhs) noexcept = default;
    149 
    150     // Preconditions: component_idx < rank
    151     constexpr reference operator[](size_t component_idx)
    152     {
    153         Expects(component_idx < Rank); // Component index must be less than rank
    154         return elems[component_idx];
    155     }
    156 
    157     // Preconditions: component_idx < rank
    158     constexpr const_reference operator[](size_t component_idx) const noexcept
    159     {
    160         Expects(component_idx < Rank); // Component index must be less than rank
    161         return elems[component_idx];
    162     }
    163 
    164     constexpr bool operator==(const index& rhs) const noexcept
    165     {
    166         return std::equal(elems, elems + rank, rhs.elems);
    167     }
    168 
    169     constexpr bool operator!=(const index& rhs) const noexcept { return !(this == rhs); }
    170 
    171     constexpr index operator+() const noexcept { return *this; }
    172 
    173     constexpr index operator-() const noexcept
    174     {
    175         index ret = *this;
    176         std::transform(ret, ret + rank, ret, std::negate<value_type>{});
    177         return ret;
    178     }
    179 
    180     constexpr index operator+(const index& rhs) const noexcept
    181     {
    182         index ret = *this;
    183         ret += rhs;
    184         return ret;
    185     }
    186 
    187     constexpr index operator-(const index& rhs) const noexcept
    188     {
    189         index ret = *this;
    190         ret -= rhs;
    191         return ret;
    192     }
    193 
    194     constexpr index& operator+=(const index& rhs) noexcept
    195     {
    196         std::transform(elems, elems + rank, rhs.elems, elems, std::plus<value_type>{});
    197         return *this;
    198     }
    199 
    200     constexpr index& operator-=(const index& rhs) noexcept
    201     {
    202         std::transform(elems, elems + rank, rhs.elems, elems, std::minus<value_type>{});
    203         return *this;
    204     }
    205 
    206     constexpr index operator*(value_type v) const noexcept
    207     {
    208         index ret = *this;
    209         ret *= v;
    210         return ret;
    211     }
    212 
    213     constexpr index operator/(value_type v) const noexcept
    214     {
    215         index ret = *this;
    216         ret /= v;
    217         return ret;
    218     }
    219 
    220     friend constexpr index operator*(value_type v, const index& rhs) noexcept { return rhs * v; }
    221 
    222     constexpr index& operator*=(value_type v) noexcept
    223     {
    224         std::transform(elems, elems + rank, elems,
    225                        [v](value_type x) { return std::multiplies<value_type>{}(x, v); });
    226         return *this;
    227     }
    228 
    229     constexpr index& operator/=(value_type v) noexcept
    230     {
    231         std::transform(elems, elems + rank, elems,
    232                        [v](value_type x) { return std::divides<value_type>{}(x, v); });
    233         return *this;
    234     }
    235 
    236 private:
    237     value_type elems[Rank] = {};
    238 };
    239 
    240 #ifndef _MSC_VER
    241 
    242 struct static_bounds_dynamic_range_t
    243 {
    244     template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
    245     constexpr operator T() const noexcept
    246     {
    247         return narrow_cast<T>(-1);
    248     }
    249 
    250     template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
    251     constexpr bool operator==(T other) const noexcept
    252     {
    253         return narrow_cast<T>(-1) == other;
    254     }
    255 
    256     template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
    257     constexpr bool operator!=(T other) const noexcept
    258     {
    259         return narrow_cast<T>(-1) != other;
    260     }
    261 };
    262 
    263 template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
    264 constexpr bool operator==(T left, static_bounds_dynamic_range_t right) noexcept
    265 {
    266     return right == left;
    267 }
    268 
    269 template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
    270 constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) noexcept
    271 {
    272     return right != left;
    273 }
    274 
    275 constexpr static_bounds_dynamic_range_t dynamic_range{};
    276 #else
    277 const std::ptrdiff_t dynamic_range = -1;
    278 #endif
    279 
    280 struct generalized_mapping_tag
    281 {
    282 };
    283 struct contiguous_mapping_tag : generalized_mapping_tag
    284 {
    285 };
    286 
    287 namespace details
    288 {
    289 
    290     template <std::ptrdiff_t Left, std::ptrdiff_t Right>
    291     struct LessThan
    292     {
    293         static const bool value = Left < Right;
    294     };
    295 
    296     template <std::ptrdiff_t... Ranges>
    297     struct BoundsRanges
    298     {
    299         using size_type = std::ptrdiff_t;
    300         static const size_type Depth = 0;
    301         static const size_type DynamicNum = 0;
    302         static const size_type CurrentRange = 1;
    303         static const size_type TotalSize = 1;
    304 
    305         // TODO : following signature is for work around VS bug
    306         template <typename OtherRange>
    307         BoundsRanges(const OtherRange&, bool /* firstLevel */)
    308         {
    309         }
    310 
    311         BoundsRanges(const std::ptrdiff_t* const) {}
    312         BoundsRanges() = default;
    313 
    314         template <typename T, size_t Dim>
    315         void serialize(T&) const
    316         {
    317         }
    318 
    319         template <typename T, size_t Dim>
    320         size_type linearize(const T&) const
    321         {
    322             return 0;
    323         }
    324 
    325         template <typename T, size_t Dim>
    326         size_type contains(const T&) const
    327         {
    328             return -1;
    329         }
    330 
    331         size_type elementNum(size_t) const noexcept { return 0; }
    332 
    333         size_type totalSize() const noexcept { return TotalSize; }
    334 
    335         bool operator==(const BoundsRanges&) const noexcept { return true; }
    336     };
    337 
    338     template <std::ptrdiff_t... RestRanges>
    339     struct BoundsRanges<dynamic_range, RestRanges...> : BoundsRanges<RestRanges...>
    340     {
    341         using Base = BoundsRanges<RestRanges...>;
    342         using size_type = std::ptrdiff_t;
    343         static const size_t Depth = Base::Depth + 1;
    344         static const size_t DynamicNum = Base::DynamicNum + 1;
    345         static const size_type CurrentRange = dynamic_range;
    346         static const size_type TotalSize = dynamic_range;
    347     private:
    348         size_type m_bound;
    349     public:
    350 
    351         BoundsRanges(const std::ptrdiff_t* const arr)
    352             : Base(arr + 1), m_bound(*arr * this->Base::totalSize())
    353         {
    354             Expects(0 <= *arr);
    355         }
    356 
    357         BoundsRanges() : m_bound(0) {}
    358 
    359         template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
    360         BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
    361                      bool /* firstLevel */ = true)
    362             : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
    363             , m_bound(other.totalSize())
    364         {
    365         }
    366 
    367         template <typename T, size_t Dim = 0>
    368         void serialize(T& arr) const
    369         {
    370             arr[Dim] = elementNum();
    371             this->Base::template serialize<T, Dim + 1>(arr);
    372         }
    373 
    374         template <typename T, size_t Dim = 0>
    375         size_type linearize(const T& arr) const
    376         {
    377             const size_type index = this->Base::totalSize() * arr[Dim];
    378             Expects(index < m_bound);
    379             return index + this->Base::template linearize<T, Dim + 1>(arr);
    380         }
    381 
    382         template <typename T, size_t Dim = 0>
    383         size_type contains(const T& arr) const
    384         {
    385             const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
    386             if (last == -1) return -1;
    387             const ptrdiff_t cur = this->Base::totalSize() * arr[Dim];
    388             return cur < m_bound ? cur + last : -1;
    389         }
    390 
    391         size_type totalSize() const noexcept { return m_bound; }
    392 
    393         size_type elementNum() const noexcept { return totalSize() / this->Base::totalSize(); }
    394 
    395         size_type elementNum(size_t dim) const noexcept
    396         {
    397             if (dim > 0)
    398                 return this->Base::elementNum(dim - 1);
    399             else
    400                 return elementNum();
    401         }
    402 
    403         bool operator==(const BoundsRanges& rhs) const noexcept
    404         {
    405             return m_bound == rhs.m_bound &&
    406                    static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
    407         }
    408     };
    409 
    410     template <std::ptrdiff_t CurRange, std::ptrdiff_t... RestRanges>
    411     struct BoundsRanges<CurRange, RestRanges...> : BoundsRanges<RestRanges...>
    412     {
    413         using Base = BoundsRanges<RestRanges...>;
    414         using size_type = std::ptrdiff_t;
    415         static const size_t Depth = Base::Depth + 1;
    416         static const size_t DynamicNum = Base::DynamicNum;
    417         static const size_type CurrentRange = CurRange;
    418         static const size_type TotalSize =
    419             Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize;
    420 
    421         BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {}
    422         BoundsRanges() = default;
    423 
    424         template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
    425         BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
    426                      bool firstLevel = true)
    427             : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
    428         {
    429             (void) firstLevel;
    430         }
    431 
    432         template <typename T, size_t Dim = 0>
    433         void serialize(T& arr) const
    434         {
    435             arr[Dim] = elementNum();
    436             this->Base::template serialize<T, Dim + 1>(arr);
    437         }
    438 
    439         template <typename T, size_t Dim = 0>
    440         size_type linearize(const T& arr) const
    441         {
    442             Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range
    443             return this->Base::totalSize() * arr[Dim] +
    444                    this->Base::template linearize<T, Dim + 1>(arr);
    445         }
    446 
    447         template <typename T, size_t Dim = 0>
    448         size_type contains(const T& arr) const
    449         {
    450             if (arr[Dim] >= CurrentRange) return -1;
    451             const size_type last = this->Base::template contains<T, Dim + 1>(arr);
    452             if (last == -1) return -1;
    453             return this->Base::totalSize() * arr[Dim] + last;
    454         }
    455 
    456         size_type totalSize() const noexcept { return CurrentRange * this->Base::totalSize(); }
    457 
    458         size_type elementNum() const noexcept { return CurrentRange; }
    459 
    460         size_type elementNum(size_t dim) const noexcept
    461         {
    462             if (dim > 0)
    463                 return this->Base::elementNum(dim - 1);
    464             else
    465                 return elementNum();
    466         }
    467 
    468         bool operator==(const BoundsRanges& rhs) const noexcept
    469         {
    470             return static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
    471         }
    472     };
    473 
    474     template <typename SourceType, typename TargetType>
    475     struct BoundsRangeConvertible
    476         : public std::integral_constant<bool, (SourceType::TotalSize >= TargetType::TotalSize ||
    477                                                TargetType::TotalSize == dynamic_range ||
    478                                                SourceType::TotalSize == dynamic_range ||
    479                                                TargetType::TotalSize == 0)>
    480     {
    481     };
    482 
    483     template <typename TypeChain>
    484     struct TypeListIndexer
    485     {
    486         const TypeChain& obj_;
    487         TypeListIndexer(const TypeChain& obj) : obj_(obj) {}
    488 
    489         template <size_t N>
    490         const TypeChain& getObj(std::true_type)
    491         {
    492             return obj_;
    493         }
    494 
    495         template <size_t N, typename MyChain = TypeChain, typename MyBase = typename MyChain::Base>
    496         auto getObj(std::false_type)
    497             -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>())
    498         {
    499             return TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>();
    500         }
    501 
    502         template <size_t N>
    503         auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, N == 0>()))
    504         {
    505             return getObj<N - 1>(std::integral_constant<bool, N == 0>());
    506         }
    507     };
    508 
    509     template <typename TypeChain>
    510     TypeListIndexer<TypeChain> createTypeListIndexer(const TypeChain& obj)
    511     {
    512         return TypeListIndexer<TypeChain>(obj);
    513     }
    514 
    515     template <size_t Rank, bool Enabled = (Rank > 1),
    516               typename Ret = std::enable_if_t<Enabled, index<Rank - 1>>>
    517     constexpr Ret shift_left(const index<Rank>& other) noexcept
    518     {
    519         Ret ret{};
    520         for (size_t i = 0; i < Rank - 1; ++i) {
    521             ret[i] = other[i + 1];
    522         }
    523         return ret;
    524     }
    525 }
    526 
    527 template <typename IndexType>
    528 class bounds_iterator;
    529 
    530 template <std::ptrdiff_t... Ranges>
    531 class static_bounds
    532 {
    533 public:
    534     static_bounds(const details::BoundsRanges<Ranges...>&) {}
    535 };
    536 
    537 template <std::ptrdiff_t FirstRange, std::ptrdiff_t... RestRanges>
    538 class static_bounds<FirstRange, RestRanges...>
    539 {
    540     using MyRanges = details::BoundsRanges<FirstRange, RestRanges...>;
    541 
    542     MyRanges m_ranges;
    543     constexpr static_bounds(const MyRanges& range) : m_ranges(range) {}
    544 
    545     template <std::ptrdiff_t... OtherRanges>
    546     friend class static_bounds;
    547 
    548 public:
    549     static const size_t rank = MyRanges::Depth;
    550     static const size_t dynamic_rank = MyRanges::DynamicNum;
    551     static const std::ptrdiff_t static_size = MyRanges::TotalSize;
    552 
    553     using size_type = std::ptrdiff_t;
    554     using index_type = index<rank>;
    555     using const_index_type = std::add_const_t<index_type>;
    556     using iterator = bounds_iterator<const_index_type>;
    557     using const_iterator = bounds_iterator<const_index_type>;
    558     using difference_type = std::ptrdiff_t;
    559     using sliced_type = static_bounds<RestRanges...>;
    560     using mapping_type = contiguous_mapping_tag;
    561 
    562     constexpr static_bounds(const static_bounds&) = default;
    563 
    564     template <typename SourceType, typename TargetType, size_t Rank>
    565     struct BoundsRangeConvertible2;
    566 
    567     template <size_t Rank, typename SourceType, typename TargetType,
    568               typename Ret = BoundsRangeConvertible2<typename SourceType::Base,
    569                                                      typename TargetType::Base, Rank>>
    570     static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret;
    571 
    572     template <size_t Rank, typename SourceType, typename TargetType>
    573     static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type;
    574 
    575     template <typename SourceType, typename TargetType, size_t Rank>
    576     struct BoundsRangeConvertible2
    577         : decltype(helpBoundsRangeConvertible<Rank - 1>(
    578               SourceType(), TargetType(),
    579               std::integral_constant<bool,
    580                                      SourceType::Depth == TargetType::Depth &&
    581                                          (SourceType::CurrentRange == TargetType::CurrentRange ||
    582                                           TargetType::CurrentRange == dynamic_range ||
    583                                           SourceType::CurrentRange == dynamic_range)>()))
    584     {
    585     };
    586 
    587     template <typename SourceType, typename TargetType>
    588     struct BoundsRangeConvertible2<SourceType, TargetType, 0> : std::true_type
    589     {
    590     };
    591 
    592     template <typename SourceType, typename TargetType, std::ptrdiff_t Rank = TargetType::Depth>
    593     struct BoundsRangeConvertible
    594         : decltype(helpBoundsRangeConvertible<Rank - 1>(
    595               SourceType(), TargetType(),
    596               std::integral_constant<bool,
    597                                      SourceType::Depth == TargetType::Depth &&
    598                                          (!details::LessThan<SourceType::CurrentRange,
    599                                                              TargetType::CurrentRange>::value ||
    600                                           TargetType::CurrentRange == dynamic_range ||
    601                                           SourceType::CurrentRange == dynamic_range)>()))
    602     {
    603     };
    604 
    605     template <typename SourceType, typename TargetType>
    606     struct BoundsRangeConvertible<SourceType, TargetType, 0> : std::true_type
    607     {
    608     };
    609 
    610     template <std::ptrdiff_t... Ranges,
    611               typename = std::enable_if_t<details::BoundsRangeConvertible<
    612                   details::BoundsRanges<Ranges...>,
    613                   details::BoundsRanges<FirstRange, RestRanges...>>::value>>
    614     constexpr static_bounds(const static_bounds<Ranges...>& other) : m_ranges(other.m_ranges)
    615     {
    616         Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges<Ranges...>::DynamicNum == 0) ||
    617                 MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize());
    618     }
    619 
    620     constexpr static_bounds(std::initializer_list<size_type> il)
    621         : m_ranges(static_cast<const std::ptrdiff_t*>(il.begin()))
    622     {
    623         // Size of the initializer list must match the rank of the array
    624         Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) ||
    625                 MyRanges::DynamicNum == il.size());
    626         // Size of the range must be less than the max element of the size type
    627         Expects(m_ranges.totalSize() <= PTRDIFF_MAX);
    628     }
    629 
    630     constexpr static_bounds() = default;
    631 
    632     constexpr sliced_type slice() const noexcept
    633     {
    634         return sliced_type{static_cast<const details::BoundsRanges<RestRanges...>&>(m_ranges)};
    635     }
    636 
    637     constexpr size_type stride() const noexcept { return rank > 1 ? slice().size() : 1; }
    638 
    639     constexpr size_type size() const noexcept { return m_ranges.totalSize(); }
    640 
    641     constexpr size_type total_size() const noexcept { return m_ranges.totalSize(); }
    642 
    643     constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); }
    644 
    645     constexpr bool contains(const index_type& idx) const noexcept
    646     {
    647         return m_ranges.contains(idx) != -1;
    648     }
    649 
    650     constexpr size_type operator[](size_t index) const noexcept
    651     {
    652         return m_ranges.elementNum(index);
    653     }
    654 
    655     template <size_t Dim = 0>
    656     constexpr size_type extent() const noexcept
    657     {
    658         static_assert(Dim < rank,
    659                       "dimension should be less than rank (dimension count starts from 0)");
    660         return details::createTypeListIndexer(m_ranges).template get<Dim>().elementNum();
    661     }
    662 
    663     template <typename IntType>
    664     constexpr size_type extent(IntType dim) const noexcept
    665     {
    666         static_assert(std::is_integral<IntType>::value,
    667                       "Dimension parameter must be supplied as an integral type.");
    668         auto real_dim = narrow_cast<size_t>(dim);
    669         Expects(real_dim < rank);
    670 
    671         return m_ranges.elementNum(real_dim);
    672     }
    673 
    674     constexpr index_type index_bounds() const noexcept
    675     {
    676         size_type extents[rank] = {};
    677         m_ranges.serialize(extents);
    678         return {extents};
    679     }
    680 
    681     template <std::ptrdiff_t... Ranges>
    682     constexpr bool operator==(const static_bounds<Ranges...>& rhs) const noexcept
    683     {
    684         return this->size() == rhs.size();
    685     }
    686 
    687     template <std::ptrdiff_t... Ranges>
    688     constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const noexcept
    689     {
    690         return !(*this == rhs);
    691     }
    692 
    693     constexpr const_iterator begin() const noexcept { return const_iterator(*this, index_type{}); }
    694 
    695     constexpr const_iterator end() const noexcept
    696     {
    697         return const_iterator(*this, this->index_bounds());
    698     }
    699 };
    700 
    701 template <size_t Rank>
    702 class strided_bounds
    703 {
    704     template <size_t OtherRank>
    705     friend class strided_bounds;
    706 
    707 public:
    708     static const size_t rank = Rank;
    709     using value_type = std::ptrdiff_t;
    710     using reference = std::add_lvalue_reference_t<value_type>;
    711     using const_reference = std::add_const_t<reference>;
    712     using size_type = value_type;
    713     using difference_type = value_type;
    714     using index_type = index<rank>;
    715     using const_index_type = std::add_const_t<index_type>;
    716     using iterator = bounds_iterator<const_index_type>;
    717     using const_iterator = bounds_iterator<const_index_type>;
    718     static const value_type dynamic_rank = rank;
    719     static const value_type static_size = dynamic_range;
    720     using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
    721     using mapping_type = generalized_mapping_tag;
    722 
    723     constexpr strided_bounds(const strided_bounds&) noexcept = default;
    724 
    725     constexpr strided_bounds& operator=(const strided_bounds&) noexcept = default;
    726 
    727     constexpr strided_bounds(const value_type (&values)[rank], index_type strides)
    728         : m_extents(values), m_strides(std::move(strides))
    729     {
    730     }
    731 
    732     constexpr strided_bounds(const index_type& extents, const index_type& strides) noexcept
    733         : m_extents(extents),
    734           m_strides(strides)
    735     {
    736     }
    737 
    738     constexpr index_type strides() const noexcept { return m_strides; }
    739 
    740     constexpr size_type total_size() const noexcept
    741     {
    742         size_type ret = 0;
    743         for (size_t i = 0; i < rank; ++i) {
    744             ret += (m_extents[i] - 1) * m_strides[i];
    745         }
    746         return ret + 1;
    747     }
    748 
    749     constexpr size_type size() const noexcept
    750     {
    751         size_type ret = 1;
    752         for (size_t i = 0; i < rank; ++i) {
    753             ret *= m_extents[i];
    754         }
    755         return ret;
    756     }
    757 
    758     constexpr bool contains(const index_type& idx) const noexcept
    759     {
    760         for (size_t i = 0; i < rank; ++i) {
    761             if (idx[i] < 0 || idx[i] >= m_extents[i]) return false;
    762         }
    763         return true;
    764     }
    765 
    766     constexpr size_type linearize(const index_type& idx) const noexcept
    767     {
    768         size_type ret = 0;
    769         for (size_t i = 0; i < rank; i++) {
    770             Expects(idx[i] < m_extents[i]); // index is out of bounds of the array
    771             ret += idx[i] * m_strides[i];
    772         }
    773         return ret;
    774     }
    775 
    776     constexpr size_type stride() const noexcept { return m_strides[0]; }
    777 
    778     template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
    779     constexpr sliced_type slice() const
    780     {
    781         return {details::shift_left(m_extents), details::shift_left(m_strides)};
    782     }
    783 
    784     template <size_t Dim = 0>
    785     constexpr size_type extent() const noexcept
    786     {
    787         static_assert(Dim < Rank,
    788                       "dimension should be less than rank (dimension count starts from 0)");
    789         return m_extents[Dim];
    790     }
    791 
    792     constexpr index_type index_bounds() const noexcept { return m_extents; }
    793     constexpr const_iterator begin() const noexcept { return const_iterator{*this, index_type{}}; }
    794 
    795     constexpr const_iterator end() const noexcept { return const_iterator{*this, index_bounds()}; }
    796 
    797 private:
    798     index_type m_extents;
    799     index_type m_strides;
    800 };
    801 
    802 template <typename T>
    803 struct is_bounds : std::integral_constant<bool, false>
    804 {
    805 };
    806 template <std::ptrdiff_t... Ranges>
    807 struct is_bounds<static_bounds<Ranges...>> : std::integral_constant<bool, true>
    808 {
    809 };
    810 template <size_t Rank>
    811 struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true>
    812 {
    813 };
    814 
    815 template <typename IndexType>
    816 class bounds_iterator : public std::iterator<std::random_access_iterator_tag, IndexType>
    817 {
    818 private:
    819     using Base = std::iterator<std::random_access_iterator_tag, IndexType>;
    820 
    821 public:
    822     static const size_t rank = IndexType::rank;
    823     using typename Base::reference;
    824     using typename Base::pointer;
    825     using typename Base::difference_type;
    826     using typename Base::value_type;
    827     using index_type = value_type;
    828     using index_size_type = typename IndexType::value_type;
    829     template <typename Bounds>
    830     explicit bounds_iterator(const Bounds& bnd, value_type curr) noexcept
    831         : boundary_(bnd.index_bounds()),
    832           curr_(std::move(curr))
    833     {
    834         static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
    835     }
    836 
    837     constexpr reference operator*() const noexcept { return curr_; }
    838 
    839     constexpr pointer operator->() const noexcept { return &curr_; }
    840 
    841     constexpr bounds_iterator& operator++() noexcept
    842     {
    843         for (size_t i = rank; i-- > 0;) {
    844             if (curr_[i] < boundary_[i] - 1) {
    845                 curr_[i]++;
    846                 return *this;
    847             }
    848             curr_[i] = 0;
    849         }
    850         // If we're here we've wrapped over - set to past-the-end.
    851         curr_ = boundary_;
    852         return *this;
    853     }
    854 
    855     constexpr bounds_iterator operator++(int) noexcept
    856     {
    857         auto ret = *this;
    858         ++(*this);
    859         return ret;
    860     }
    861 
    862     constexpr bounds_iterator& operator--() noexcept
    863     {
    864         if (!less(curr_, boundary_)) {
    865             // if at the past-the-end, set to last element
    866             for (size_t i = 0; i < rank; ++i) {
    867                 curr_[i] = boundary_[i] - 1;
    868             }
    869             return *this;
    870         }
    871         for (size_t i = rank; i-- > 0;) {
    872             if (curr_[i] >= 1) {
    873                 curr_[i]--;
    874                 return *this;
    875             }
    876             curr_[i] = boundary_[i] - 1;
    877         }
    878         // If we're here the preconditions were violated
    879         // "pre: there exists s such that r == ++s"
    880         Expects(false);
    881         return *this;
    882     }
    883 
    884     constexpr bounds_iterator operator--(int) noexcept
    885     {
    886         auto ret = *this;
    887         --(*this);
    888         return ret;
    889     }
    890 
    891     constexpr bounds_iterator operator+(difference_type n) const noexcept
    892     {
    893         bounds_iterator ret{*this};
    894         return ret += n;
    895     }
    896 
    897     constexpr bounds_iterator& operator+=(difference_type n) noexcept
    898     {
    899         auto linear_idx = linearize(curr_) + n;
    900         std::remove_const_t<value_type> stride = 0;
    901         stride[rank - 1] = 1;
    902         for (size_t i = rank - 1; i-- > 0;) {
    903             stride[i] = stride[i + 1] * boundary_[i + 1];
    904         }
    905         for (size_t i = 0; i < rank; ++i) {
    906             curr_[i] = linear_idx / stride[i];
    907             linear_idx = linear_idx % stride[i];
    908         }
    909         // index is out of bounds of the array
    910         Expects(!less(curr_, index_type{}) && !less(boundary_, curr_));
    911         return *this;
    912     }
    913 
    914     constexpr bounds_iterator operator-(difference_type n) const noexcept
    915     {
    916         bounds_iterator ret{*this};
    917         return ret -= n;
    918     }
    919 
    920     constexpr bounds_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
    921 
    922     constexpr difference_type operator-(const bounds_iterator& rhs) const noexcept
    923     {
    924         return linearize(curr_) - linearize(rhs.curr_);
    925     }
    926 
    927     constexpr value_type operator[](difference_type n) const noexcept { return *(*this + n); }
    928 
    929     constexpr bool operator==(const bounds_iterator& rhs) const noexcept
    930     {
    931         return curr_ == rhs.curr_;
    932     }
    933 
    934     constexpr bool operator!=(const bounds_iterator& rhs) const noexcept { return !(*this == rhs); }
    935 
    936     constexpr bool operator<(const bounds_iterator& rhs) const noexcept
    937     {
    938         return less(curr_, rhs.curr_);
    939     }
    940 
    941     constexpr bool operator<=(const bounds_iterator& rhs) const noexcept { return !(rhs < *this); }
    942 
    943     constexpr bool operator>(const bounds_iterator& rhs) const noexcept { return rhs < *this; }
    944 
    945     constexpr bool operator>=(const bounds_iterator& rhs) const noexcept { return !(rhs > *this); }
    946 
    947     void swap(bounds_iterator& rhs) noexcept
    948     {
    949         std::swap(boundary_, rhs.boundary_);
    950         std::swap(curr_, rhs.curr_);
    951     }
    952 
    953 private:
    954     constexpr bool less(index_type& one, index_type& other) const noexcept
    955     {
    956         for (size_t i = 0; i < rank; ++i) {
    957             if (one[i] < other[i]) return true;
    958         }
    959         return false;
    960     }
    961 
    962     constexpr index_size_type linearize(const value_type& idx) const noexcept
    963     {
    964         // TODO: Smarter impl.
    965         // Check if past-the-end
    966         index_size_type multiplier = 1;
    967         index_size_type res = 0;
    968         if (!less(idx, boundary_)) {
    969             res = 1;
    970             for (size_t i = rank; i-- > 0;) {
    971                 res += (idx[i] - 1) * multiplier;
    972                 multiplier *= boundary_[i];
    973             }
    974         }
    975         else
    976         {
    977             for (size_t i = rank; i-- > 0;) {
    978                 res += idx[i] * multiplier;
    979                 multiplier *= boundary_[i];
    980             }
    981         }
    982         return res;
    983     }
    984 
    985     value_type boundary_;
    986     std::remove_const_t<value_type> curr_;
    987 };
    988 
    989 template <typename IndexType>
    990 bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n,
    991                                      const bounds_iterator<IndexType>& rhs) noexcept
    992 {
    993     return rhs + n;
    994 }
    995 
    996 namespace details
    997 {
    998     template <typename Bounds>
    999     constexpr std::enable_if_t<
   1000         std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value,
   1001         typename Bounds::index_type>
   1002     make_stride(const Bounds& bnd) noexcept
   1003     {
   1004         return bnd.strides();
   1005     }
   1006 
   1007     // Make a stride vector from bounds, assuming contiguous memory.
   1008     template <typename Bounds>
   1009     constexpr std::enable_if_t<
   1010         std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::value,
   1011         typename Bounds::index_type>
   1012     make_stride(const Bounds& bnd) noexcept
   1013     {
   1014         auto extents = bnd.index_bounds();
   1015         typename Bounds::size_type stride[Bounds::rank] = {};
   1016 
   1017         stride[Bounds::rank - 1] = 1;
   1018         for (size_t i = 1; i < Bounds::rank; ++i) {
   1019             stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i];
   1020         }
   1021         return {stride};
   1022     }
   1023 
   1024     template <typename BoundsSrc, typename BoundsDest>
   1025     void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest)
   1026     {
   1027         static_assert(is_bounds<BoundsSrc>::value && is_bounds<BoundsDest>::value,
   1028                       "The src type and dest type must be bounds");
   1029         static_assert(std::is_same<typename BoundsSrc::mapping_type, contiguous_mapping_tag>::value,
   1030                       "The source type must be a contiguous bounds");
   1031         static_assert(BoundsDest::static_size == dynamic_range ||
   1032                           BoundsSrc::static_size == dynamic_range ||
   1033                           BoundsDest::static_size == BoundsSrc::static_size,
   1034                       "The source bounds must have same size as dest bounds");
   1035         Expects(src.size() == dest.size());
   1036     }
   1037 
   1038 } // namespace details
   1039 
   1040 template <typename Span>
   1041 class contiguous_span_iterator;
   1042 template <typename Span>
   1043 class general_span_iterator;
   1044 
   1045 template <std::ptrdiff_t DimSize = dynamic_range>
   1046 struct dim_t
   1047 {
   1048     static const std::ptrdiff_t value = DimSize;
   1049 };
   1050 template <>
   1051 struct dim_t<dynamic_range>
   1052 {
   1053     static const std::ptrdiff_t value = dynamic_range;
   1054     const std::ptrdiff_t dvalue;
   1055     dim_t(std::ptrdiff_t size) : dvalue(size) {}
   1056 };
   1057 
   1058 template <std::ptrdiff_t N>
   1059 constexpr std::enable_if_t<(N >= 0), dim_t<N>> dim() noexcept
   1060 {
   1061     return dim_t<N>();
   1062 }
   1063 
   1064 template <std::ptrdiff_t N = dynamic_range>
   1065 constexpr std::enable_if_t<N == dynamic_range, dim_t<N>> dim(std::ptrdiff_t n) noexcept
   1066 {
   1067     return dim_t<>(n);
   1068 }
   1069 
   1070 template <typename ValueType, std::ptrdiff_t FirstDimension = dynamic_range,
   1071           std::ptrdiff_t... RestDimensions>
   1072 class multi_span;
   1073 
   1074 template <typename ValueType, size_t Rank>
   1075 class strided_span;
   1076 
   1077 namespace details
   1078 {
   1079     template <typename T, typename = std::true_type>
   1080     struct SpanTypeTraits
   1081     {
   1082         using value_type = T;
   1083         using size_type = size_t;
   1084     };
   1085 
   1086     template <typename Traits>
   1087     struct SpanTypeTraits<Traits, typename std::is_reference<typename Traits::span_traits&>::type>
   1088     {
   1089         using value_type = typename Traits::span_traits::value_type;
   1090         using size_type = typename Traits::span_traits::size_type;
   1091     };
   1092 
   1093     template <typename T, std::ptrdiff_t... Ranks>
   1094     struct SpanArrayTraits
   1095     {
   1096         using type = multi_span<T, Ranks...>;
   1097         using value_type = T;
   1098         using bounds_type = static_bounds<Ranks...>;
   1099         using pointer = T*;
   1100         using reference = T&;
   1101     };
   1102     template <typename T, std::ptrdiff_t N, std::ptrdiff_t... Ranks>
   1103     struct SpanArrayTraits<T[N], Ranks...> : SpanArrayTraits<T, Ranks..., N>
   1104     {
   1105     };
   1106 
   1107     template <typename BoundsType>
   1108     BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size
   1109     {
   1110         Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX);
   1111         return BoundsType{totalSize};
   1112     }
   1113     template <typename BoundsType>
   1114     BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size
   1115     {
   1116         Expects(BoundsType::static_size <= totalSize);
   1117         return {};
   1118     }
   1119     template <typename BoundsType>
   1120     BoundsType newBoundsHelper(std::ptrdiff_t totalSize)
   1121     {
   1122         static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1");
   1123         return newBoundsHelperImpl<BoundsType>(
   1124             totalSize, std::integral_constant<bool, BoundsType::dynamic_rank == 1>());
   1125     }
   1126 
   1127     struct Sep
   1128     {
   1129     };
   1130 
   1131     template <typename T, typename... Args>
   1132     T static_as_multi_span_helper(Sep, Args... args)
   1133     {
   1134         return T{narrow_cast<typename T::size_type>(args)...};
   1135     }
   1136     template <typename T, typename Arg, typename... Args>
   1137     std::enable_if_t<
   1138         !std::is_same<Arg, dim_t<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T>
   1139         static_as_multi_span_helper(Arg, Args... args)
   1140     {
   1141         return static_as_multi_span_helper<T>(args...);
   1142     }
   1143     template <typename T, typename... Args>
   1144     T static_as_multi_span_helper(dim_t<dynamic_range> val, Args... args)
   1145     {
   1146         return static_as_multi_span_helper<T>(args..., val.dvalue);
   1147     }
   1148 
   1149     template <typename... Dimensions>
   1150     struct static_as_multi_span_static_bounds_helper
   1151     {
   1152         using type = static_bounds<(Dimensions::value)...>;
   1153     };
   1154 
   1155     template <typename T>
   1156     struct is_multi_span_oracle : std::false_type
   1157     {
   1158     };
   1159 
   1160     template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
   1161     struct is_multi_span_oracle<multi_span<ValueType, FirstDimension, RestDimensions...>>
   1162         : std::true_type
   1163     {
   1164     };
   1165 
   1166     template <typename ValueType, std::ptrdiff_t Rank>
   1167     struct is_multi_span_oracle<strided_span<ValueType, Rank>> : std::true_type
   1168     {
   1169     };
   1170 
   1171     template <typename T>
   1172     struct is_multi_span : is_multi_span_oracle<std::remove_cv_t<T>>
   1173     {
   1174     };
   1175 }
   1176 
   1177 template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
   1178 class multi_span
   1179 {
   1180     // TODO do we still need this?
   1181     template <typename ValueType2, std::ptrdiff_t FirstDimension2,
   1182               std::ptrdiff_t... RestDimensions2>
   1183     friend class multi_span;
   1184 
   1185 public:
   1186     using bounds_type = static_bounds<FirstDimension, RestDimensions...>;
   1187     static const size_t Rank = bounds_type::rank;
   1188     using size_type = typename bounds_type::size_type;
   1189     using index_type = typename bounds_type::index_type;
   1190     using value_type = ValueType;
   1191     using const_value_type = std::add_const_t<value_type>;
   1192     using pointer = std::add_pointer_t<value_type>;
   1193     using reference = std::add_lvalue_reference_t<value_type>;
   1194     using iterator = contiguous_span_iterator<multi_span>;
   1195     using const_span = multi_span<const_value_type, FirstDimension, RestDimensions...>;
   1196     using const_iterator = contiguous_span_iterator<const_span>;
   1197     using reverse_iterator = std::reverse_iterator<iterator>;
   1198     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   1199     using sliced_type =
   1200         std::conditional_t<Rank == 1, value_type, multi_span<value_type, RestDimensions...>>;
   1201 
   1202 private:
   1203     pointer data_;
   1204     bounds_type bounds_;
   1205 
   1206     friend iterator;
   1207     friend const_iterator;
   1208 
   1209 public:
   1210     // default constructor - same as constructing from nullptr_t
   1211     constexpr multi_span() noexcept : multi_span(nullptr, bounds_type{})
   1212     {
   1213         static_assert(bounds_type::dynamic_rank != 0 ||
   1214                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
   1215                       "Default construction of multi_span<T> only possible "
   1216                       "for dynamic or fixed, zero-length spans.");
   1217     }
   1218 
   1219     // construct from nullptr - get an empty multi_span
   1220     constexpr multi_span(std::nullptr_t) noexcept : multi_span(nullptr, bounds_type{})
   1221     {
   1222         static_assert(bounds_type::dynamic_rank != 0 ||
   1223                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
   1224                       "nullptr_t construction of multi_span<T> only possible "
   1225                       "for dynamic or fixed, zero-length spans.");
   1226     }
   1227 
   1228     // construct from nullptr with size of 0 (helps with template function calls)
   1229     template <class IntType, typename = std::enable_if_t<std::is_integral<IntType>::value>>
   1230     constexpr multi_span(std::nullptr_t, IntType size) noexcept : multi_span(nullptr, bounds_type{})
   1231     {
   1232         static_assert(bounds_type::dynamic_rank != 0 ||
   1233                           (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
   1234                       "nullptr_t construction of multi_span<T> only possible "
   1235                       "for dynamic or fixed, zero-length spans.");
   1236         Expects(size == 0);
   1237     }
   1238 
   1239     // construct from a single element
   1240     constexpr multi_span(reference data) noexcept : multi_span(&data, bounds_type{1})
   1241     {
   1242         static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 ||
   1243                           bounds_type::static_size == 1,
   1244                       "Construction from a single element only possible "
   1245                       "for dynamic or fixed spans of length 0 or 1.");
   1246     }
   1247 
   1248     // prevent constructing from temporaries for single-elements
   1249     constexpr multi_span(value_type&&) = delete;
   1250 
   1251     // construct from pointer + length
   1252     constexpr multi_span(pointer ptr, size_type size) noexcept : multi_span(ptr, bounds_type{size})
   1253     {
   1254     }
   1255 
   1256     // construct from pointer + length - multidimensional
   1257     constexpr multi_span(pointer data, bounds_type bounds) noexcept : data_(data),
   1258                                                                       bounds_(std::move(bounds))
   1259     {
   1260         Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0);
   1261     }
   1262 
   1263     // construct from begin,end pointer pair
   1264     template <typename Ptr,
   1265               typename = std::enable_if_t<std::is_convertible<Ptr, pointer>::value &&
   1266                                           details::LessThan<bounds_type::dynamic_rank, 2>::value>>
   1267     constexpr multi_span(pointer begin, Ptr end)
   1268         : multi_span(begin,
   1269                      details::newBoundsHelper<bounds_type>(static_cast<pointer>(end) - begin))
   1270     {
   1271         Expects(begin != nullptr && end != nullptr && begin <= static_cast<pointer>(end));
   1272     }
   1273 
   1274     // construct from n-dimensions static array
   1275     template <typename T, size_t N, typename Helper = details::SpanArrayTraits<T, N>>
   1276     constexpr multi_span(T (&arr)[N])
   1277         : multi_span(reinterpret_cast<pointer>(arr), bounds_type{typename Helper::bounds_type{}})
   1278     {
   1279         static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
   1280                       "Cannot convert from source type to target multi_span type.");
   1281         static_assert(std::is_convertible<typename Helper::bounds_type, bounds_type>::value,
   1282                       "Cannot construct a multi_span from an array with fewer elements.");
   1283     }
   1284 
   1285     // construct from n-dimensions dynamic array (e.g. new int[m][4])
   1286     // (precedence will be lower than the 1-dimension pointer)
   1287     template <typename T, typename Helper = details::SpanArrayTraits<T, dynamic_range>>
   1288     constexpr multi_span(T* const& data, size_type size)
   1289         : multi_span(reinterpret_cast<pointer>(data), typename Helper::bounds_type{size})
   1290     {
   1291         static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
   1292                       "Cannot convert from source type to target multi_span type.");
   1293     }
   1294 
   1295     // construct from std::array
   1296     template <typename T, size_t N>
   1297     constexpr multi_span(std::array<T, N>& arr)
   1298         : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
   1299     {
   1300         static_assert(
   1301             std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
   1302             "Cannot convert from source type to target multi_span type.");
   1303         static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
   1304                       "You cannot construct a multi_span from a std::array of smaller size.");
   1305     }
   1306 
   1307     // construct from const std::array
   1308     template <typename T, size_t N>
   1309     constexpr multi_span(const std::array<std::remove_const_t<value_type>, N>& arr)
   1310         : multi_span(arr.data(), static_bounds<N>())
   1311     {
   1312         static_assert(std::is_convertible<T(*)[], std::remove_const_t<value_type>>::value,
   1313                       "Cannot convert from source type to target multi_span type.");
   1314         static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
   1315                       "You cannot construct a multi_span from a std::array of smaller size.");
   1316     }
   1317 
   1318     // prevent constructing from temporary std::array
   1319     template <typename T, size_t N>
   1320     constexpr multi_span(std::array<T, N>&& arr) = delete;
   1321 
   1322     // construct from containers
   1323     // future: could use contiguous_iterator_traits to identify only contiguous containers
   1324     // type-requirements: container must have .size(), operator[] which are value_type compatible
   1325     template <typename Cont, typename DataType = typename Cont::value_type,
   1326               typename = std::enable_if_t<
   1327                   !details::is_multi_span<Cont>::value &&
   1328                   std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
   1329                   std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
   1330                                                      *std::declval<Cont>().data())>,
   1331                                DataType>::value>>
   1332     constexpr multi_span(Cont& cont)
   1333         : multi_span(static_cast<pointer>(cont.data()),
   1334                      details::newBoundsHelper<bounds_type>(narrow_cast<size_type>(cont.size())))
   1335     {
   1336     }
   1337 
   1338     // prevent constructing from temporary containers
   1339     template <typename Cont, typename DataType = typename Cont::value_type,
   1340               typename = std::enable_if_t<
   1341                   !details::is_multi_span<Cont>::value &&
   1342                   std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
   1343                   std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
   1344                                                      *std::declval<Cont>().data())>,
   1345                                DataType>::value>>
   1346     explicit constexpr multi_span(Cont&& cont) = delete;
   1347 
   1348     // construct from a convertible multi_span
   1349     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1350               typename OtherBounds = static_bounds<OtherDimensions...>,
   1351               typename = std::enable_if_t<std::is_convertible<OtherValueType, ValueType>::value &&
   1352                                           std::is_convertible<OtherBounds, bounds_type>::value>>
   1353     constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other) noexcept
   1354         : data_(other.data_),
   1355           bounds_(other.bounds_)
   1356     {
   1357     }
   1358 
   1359 // trivial copy and move
   1360 #ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT
   1361     constexpr multi_span(multi_span&&) = default;
   1362 #endif
   1363     constexpr multi_span(const multi_span&) = default;
   1364 
   1365 // trivial assignment
   1366 #ifndef GSL_MSVC_NO_SUPPORT_FOR_MOVE_CTOR_DEFAULT
   1367     constexpr multi_span& operator=(multi_span&&) = default;
   1368 #endif
   1369     constexpr multi_span& operator=(const multi_span&) = default;
   1370 
   1371     // first() - extract the first Count elements into a new multi_span
   1372     template <std::ptrdiff_t Count>
   1373     constexpr multi_span<ValueType, Count> first() const noexcept
   1374     {
   1375         static_assert(Count >= 0, "Count must be >= 0.");
   1376         static_assert(bounds_type::static_size == dynamic_range ||
   1377                           Count <= bounds_type::static_size,
   1378                       "Count is out of bounds.");
   1379 
   1380         Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
   1381         return {this->data(), Count};
   1382     }
   1383 
   1384     // first() - extract the first count elements into a new multi_span
   1385     constexpr multi_span<ValueType, dynamic_range> first(size_type count) const noexcept
   1386     {
   1387         Expects(count >= 0 && count <= this->size());
   1388         return {this->data(), count};
   1389     }
   1390 
   1391     // last() - extract the last Count elements into a new multi_span
   1392     template <std::ptrdiff_t Count>
   1393     constexpr multi_span<ValueType, Count> last() const noexcept
   1394     {
   1395         static_assert(Count >= 0, "Count must be >= 0.");
   1396         static_assert(bounds_type::static_size == dynamic_range ||
   1397                           Count <= bounds_type::static_size,
   1398                       "Count is out of bounds.");
   1399 
   1400         Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
   1401         return {this->data() + this->size() - Count, Count};
   1402     }
   1403 
   1404     // last() - extract the last count elements into a new multi_span
   1405     constexpr multi_span<ValueType, dynamic_range> last(size_type count) const noexcept
   1406     {
   1407         Expects(count >= 0 && count <= this->size());
   1408         return {this->data() + this->size() - count, count};
   1409     }
   1410 
   1411     // subspan() - create a subview of Count elements starting at Offset
   1412     template <std::ptrdiff_t Offset, std::ptrdiff_t Count>
   1413     constexpr multi_span<ValueType, Count> subspan() const noexcept
   1414     {
   1415         static_assert(Count >= 0, "Count must be >= 0.");
   1416         static_assert(Offset >= 0, "Offset must be >= 0.");
   1417         static_assert(bounds_type::static_size == dynamic_range ||
   1418                           ((Offset <= bounds_type::static_size) &&
   1419                            Count <= bounds_type::static_size - Offset),
   1420                       "You must describe a sub-range within bounds of the multi_span.");
   1421 
   1422         Expects(bounds_type::static_size != dynamic_range ||
   1423                 (Offset <= this->size() && Count <= this->size() - Offset));
   1424         return {this->data() + Offset, Count};
   1425     }
   1426 
   1427     // subspan() - create a subview of count elements starting at offset
   1428     // supplying dynamic_range for count will consume all available elements from offset
   1429     constexpr multi_span<ValueType, dynamic_range> subspan(size_type offset,
   1430                                                            size_type count = dynamic_range) const
   1431         noexcept
   1432     {
   1433         Expects((offset >= 0 && offset <= this->size()) &&
   1434                 (count == dynamic_range || (count <= this->size() - offset)));
   1435         return {this->data() + offset, count == dynamic_range ? this->length() - offset : count};
   1436     }
   1437 
   1438     // section - creates a non-contiguous, strided multi_span from a contiguous one
   1439     constexpr strided_span<ValueType, Rank> section(index_type origin, index_type extents) const
   1440         noexcept
   1441     {
   1442         size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
   1443         return {&this->operator[](origin), size,
   1444                 strided_bounds<Rank>{extents, details::make_stride(bounds())}};
   1445     }
   1446 
   1447     // length of the multi_span in elements
   1448     constexpr size_type size() const noexcept { return bounds_.size(); }
   1449 
   1450     // length of the multi_span in elements
   1451     constexpr size_type length() const noexcept { return this->size(); }
   1452 
   1453     // length of the multi_span in bytes
   1454     constexpr size_type size_bytes() const noexcept { return sizeof(value_type) * this->size(); }
   1455 
   1456     // length of the multi_span in bytes
   1457     constexpr size_type length_bytes() const noexcept { return this->size_bytes(); }
   1458 
   1459     constexpr bool empty() const noexcept { return this->size() == 0; }
   1460 
   1461     static constexpr std::size_t rank() { return Rank; }
   1462 
   1463     template <size_t Dim = 0>
   1464     constexpr size_type extent() const noexcept
   1465     {
   1466         static_assert(Dim < Rank,
   1467                       "Dimension should be less than rank (dimension count starts from 0).");
   1468         return bounds_.template extent<Dim>();
   1469     }
   1470 
   1471     template <typename IntType>
   1472     constexpr size_type extent(IntType dim) const noexcept
   1473     {
   1474         return bounds_.extent(dim);
   1475     }
   1476 
   1477     constexpr bounds_type bounds() const noexcept { return bounds_; }
   1478 
   1479     constexpr pointer data() const noexcept { return data_; }
   1480 
   1481     template <typename FirstIndex>
   1482     constexpr reference operator()(FirstIndex index)
   1483     {
   1484         return this->operator[](narrow_cast<std::ptrdiff_t>(index));
   1485     }
   1486 
   1487     template <typename FirstIndex, typename... OtherIndices>
   1488     constexpr reference operator()(FirstIndex index, OtherIndices... indices)
   1489     {
   1490         index_type idx = {narrow_cast<std::ptrdiff_t>(index),
   1491                           narrow_cast<std::ptrdiff_t>(indices...)};
   1492         return this->operator[](idx);
   1493     }
   1494 
   1495     constexpr reference operator[](const index_type& idx) const noexcept
   1496     {
   1497         return data_[bounds_.linearize(idx)];
   1498     }
   1499 
   1500     template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
   1501     constexpr Ret operator[](size_type idx) const noexcept
   1502     {
   1503         Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array
   1504         const size_type ridx = idx * bounds_.stride();
   1505 
   1506         // index is out of bounds of the underlying data
   1507         Expects(ridx < bounds_.total_size());
   1508         return Ret{data_ + ridx, bounds_.slice()};
   1509     }
   1510 
   1511     constexpr iterator begin() const noexcept { return iterator{this, true}; }
   1512 
   1513     constexpr iterator end() const noexcept { return iterator{this, false}; }
   1514 
   1515     constexpr const_iterator cbegin() const noexcept
   1516     {
   1517         return const_iterator{reinterpret_cast<const const_span*>(this), true};
   1518     }
   1519 
   1520     constexpr const_iterator cend() const noexcept
   1521     {
   1522         return const_iterator{reinterpret_cast<const const_span*>(this), false};
   1523     }
   1524 
   1525     constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
   1526 
   1527     constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
   1528 
   1529     constexpr const_reverse_iterator crbegin() const noexcept
   1530     {
   1531         return const_reverse_iterator{cend()};
   1532     }
   1533 
   1534     constexpr const_reverse_iterator crend() const noexcept
   1535     {
   1536         return const_reverse_iterator{cbegin()};
   1537     }
   1538 
   1539     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1540               typename Dummy = std::enable_if_t<std::is_same<
   1541                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1542     constexpr bool operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1543         noexcept
   1544     {
   1545         return bounds_.size() == other.bounds_.size() &&
   1546                (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
   1547     }
   1548 
   1549     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1550               typename Dummy = std::enable_if_t<std::is_same<
   1551                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1552     constexpr bool operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1553         noexcept
   1554     {
   1555         return !(*this == other);
   1556     }
   1557 
   1558     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1559               typename Dummy = std::enable_if_t<std::is_same<
   1560                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1561     constexpr bool operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1562         noexcept
   1563     {
   1564         return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
   1565     }
   1566 
   1567     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1568               typename Dummy = std::enable_if_t<std::is_same<
   1569                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1570     constexpr bool operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1571         noexcept
   1572     {
   1573         return !(other < *this);
   1574     }
   1575 
   1576     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1577               typename Dummy = std::enable_if_t<std::is_same<
   1578                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1579     constexpr bool operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1580         noexcept
   1581     {
   1582         return (other < *this);
   1583     }
   1584 
   1585     template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
   1586               typename Dummy = std::enable_if_t<std::is_same<
   1587                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1588     constexpr bool operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const
   1589         noexcept
   1590     {
   1591         return !(*this < other);
   1592     }
   1593 };
   1594 
   1595 //
   1596 // Free functions for manipulating spans
   1597 //
   1598 
   1599 // reshape a multi_span into a different dimensionality
   1600 // DimCount and Enabled here are workarounds for a bug in MSVC 2015
   1601 template <typename SpanType, typename... Dimensions2, size_t DimCount = sizeof...(Dimensions2),
   1602           bool Enabled = (DimCount > 0), typename = std::enable_if_t<Enabled>>
   1603 constexpr auto as_multi_span(SpanType s, Dimensions2... dims)
   1604     -> multi_span<typename SpanType::value_type, Dimensions2::value...>
   1605 {
   1606     static_assert(details::is_multi_span<SpanType>::value,
   1607                   "Variadic as_multi_span() is for reshaping existing spans.");
   1608     using BoundsType =
   1609         typename multi_span<typename SpanType::value_type, (Dimensions2::value)...>::bounds_type;
   1610     auto tobounds = details::static_as_multi_span_helper<BoundsType>(dims..., details::Sep{});
   1611     details::verifyBoundsReshape(s.bounds(), tobounds);
   1612     return {s.data(), tobounds};
   1613 }
   1614 
   1615 // convert a multi_span<T> to a multi_span<const byte>
   1616 template <typename U, std::ptrdiff_t... Dimensions>
   1617 multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) noexcept
   1618 {
   1619     static_assert(std::is_trivial<std::decay_t<U>>::value,
   1620                   "The value_type of multi_span must be a trivial type.");
   1621     return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
   1622 }
   1623 
   1624 // convert a multi_span<T> to a multi_span<byte> (a writeable byte multi_span)
   1625 // this is not currently a portable function that can be relied upon to work
   1626 // on all implementations. It should be considered an experimental extension
   1627 // to the standard GSL interface.
   1628 template <typename U, std::ptrdiff_t... Dimensions>
   1629 multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) noexcept
   1630 {
   1631     static_assert(std::is_trivial<std::decay_t<U>>::value,
   1632                   "The value_type of multi_span must be a trivial type.");
   1633     return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
   1634 }
   1635 
   1636 // convert a multi_span<const byte> to a multi_span<const T>
   1637 // this is not currently a portable function that can be relied upon to work
   1638 // on all implementations. It should be considered an experimental extension
   1639 // to the standard GSL interface.
   1640 template <typename U, std::ptrdiff_t... Dimensions>
   1641 constexpr auto as_multi_span(multi_span<const byte, Dimensions...> s) noexcept -> multi_span<
   1642     const U, static_cast<std::ptrdiff_t>(
   1643                  multi_span<const byte, Dimensions...>::bounds_type::static_size != dynamic_range
   1644                      ? (static_cast<size_t>(
   1645                             multi_span<const byte, Dimensions...>::bounds_type::static_size) /
   1646                         sizeof(U))
   1647                      : dynamic_range)>
   1648 {
   1649     using ConstByteSpan = multi_span<const byte, Dimensions...>;
   1650     static_assert(
   1651         std::is_trivial<std::decay_t<U>>::value &&
   1652             (ConstByteSpan::bounds_type::static_size == dynamic_range ||
   1653              ConstByteSpan::bounds_type::static_size % narrow_cast<std::ptrdiff_t>(sizeof(U)) == 0),
   1654         "Target type must be a trivial type and its size must match the byte array size");
   1655 
   1656     Expects((s.size_bytes() % sizeof(U)) == 0 && (s.size_bytes() / sizeof(U)) < PTRDIFF_MAX);
   1657     return {reinterpret_cast<const U*>(s.data()),
   1658             s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
   1659 }
   1660 
   1661 // convert a multi_span<byte> to a multi_span<T>
   1662 // this is not currently a portable function that can be relied upon to work
   1663 // on all implementations. It should be considered an experimental extension
   1664 // to the standard GSL interface.
   1665 template <typename U, std::ptrdiff_t... Dimensions>
   1666 constexpr auto as_multi_span(multi_span<byte, Dimensions...> s) noexcept
   1667     -> multi_span<U, narrow_cast<std::ptrdiff_t>(
   1668                          multi_span<byte, Dimensions...>::bounds_type::static_size != dynamic_range
   1669                              ? static_cast<std::size_t>(
   1670                                    multi_span<byte, Dimensions...>::bounds_type::static_size) /
   1671                                    sizeof(U)
   1672                              : dynamic_range)>
   1673 {
   1674     using ByteSpan = multi_span<byte, Dimensions...>;
   1675     static_assert(
   1676         std::is_trivial<std::decay_t<U>>::value &&
   1677             (ByteSpan::bounds_type::static_size == dynamic_range ||
   1678              ByteSpan::bounds_type::static_size % static_cast<std::size_t>(sizeof(U)) == 0),
   1679         "Target type must be a trivial type and its size must match the byte array size");
   1680 
   1681     Expects((s.size_bytes() % sizeof(U)) == 0);
   1682     return {reinterpret_cast<U*>(s.data()),
   1683             s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
   1684 }
   1685 
   1686 template <typename T, std::ptrdiff_t... Dimensions>
   1687 constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args)
   1688     -> multi_span<std::remove_all_extents_t<T>, Dimensions...>
   1689 {
   1690     return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr),
   1691             details::static_as_multi_span_helper<static_bounds<Dimensions...>>(args...,
   1692                                                                                details::Sep{})};
   1693 }
   1694 
   1695 template <typename T>
   1696 constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) ->
   1697     typename details::SpanArrayTraits<T, dynamic_range>::type
   1698 {
   1699     return {reinterpret_cast<std::remove_all_extents_t<T>*>(arr), len};
   1700 }
   1701 
   1702 template <typename T, size_t N>
   1703 constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type
   1704 {
   1705     return {arr};
   1706 }
   1707 
   1708 template <typename T, size_t N>
   1709 constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr)
   1710 {
   1711     return {arr};
   1712 }
   1713 
   1714 template <typename T, size_t N>
   1715 constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete;
   1716 
   1717 template <typename T, size_t N>
   1718 constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr)
   1719 {
   1720     return {arr};
   1721 }
   1722 
   1723 template <typename T>
   1724 constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end)
   1725 {
   1726     return {begin, end};
   1727 }
   1728 
   1729 template <typename Cont>
   1730 constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t<
   1731     !details::is_multi_span<std::decay_t<Cont>>::value,
   1732     multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
   1733 {
   1734     Expects(arr.size() < PTRDIFF_MAX);
   1735     return {arr.data(), narrow_cast<std::ptrdiff_t>(arr.size())};
   1736 }
   1737 
   1738 template <typename Cont>
   1739 constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t<
   1740     !details::is_multi_span<std::decay_t<Cont>>::value,
   1741     multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete;
   1742 
   1743 // from basic_string which doesn't have nonconst .data() member like other contiguous containers
   1744 template <typename CharT, typename Traits, typename Allocator>
   1745 constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str)
   1746     -> multi_span<CharT, dynamic_range>
   1747 {
   1748     Expects(str.size() < PTRDIFF_MAX);
   1749     return {&str[0], narrow_cast<std::ptrdiff_t>(str.size())};
   1750 }
   1751 
   1752 // strided_span is an extension that is not strictly part of the GSL at this time.
   1753 // It is kept here while the multidimensional interface is still being defined.
   1754 template <typename ValueType, size_t Rank>
   1755 class strided_span
   1756 {
   1757 public:
   1758     using bounds_type = strided_bounds<Rank>;
   1759     using size_type = typename bounds_type::size_type;
   1760     using index_type = typename bounds_type::index_type;
   1761     using value_type = ValueType;
   1762     using const_value_type = std::add_const_t<value_type>;
   1763     using pointer = std::add_pointer_t<value_type>;
   1764     using reference = std::add_lvalue_reference_t<value_type>;
   1765     using iterator = general_span_iterator<strided_span>;
   1766     using const_strided_span = strided_span<const_value_type, Rank>;
   1767     using const_iterator = general_span_iterator<const_strided_span>;
   1768     using reverse_iterator = std::reverse_iterator<iterator>;
   1769     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
   1770     using sliced_type =
   1771         std::conditional_t<Rank == 1, value_type, strided_span<value_type, Rank - 1>>;
   1772 
   1773 private:
   1774     pointer data_;
   1775     bounds_type bounds_;
   1776 
   1777     friend iterator;
   1778     friend const_iterator;
   1779     template <typename OtherValueType, size_t OtherRank>
   1780     friend class strided_span;
   1781 
   1782 public:
   1783     // from raw data
   1784     constexpr strided_span(pointer ptr, size_type size, bounds_type bounds)
   1785         : data_(ptr), bounds_(std::move(bounds))
   1786     {
   1787         Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0);
   1788         // Bounds cross data boundaries
   1789         Expects(this->bounds().total_size() <= size);
   1790         (void) size;
   1791     }
   1792 
   1793     // from static array of size N
   1794     template <size_type N>
   1795     constexpr strided_span(value_type (&values)[N], bounds_type bounds)
   1796         : strided_span(values, N, std::move(bounds))
   1797     {
   1798     }
   1799 
   1800     // from array view
   1801     template <typename OtherValueType, std::ptrdiff_t... Dimensions,
   1802               bool Enabled1 = (sizeof...(Dimensions) == Rank),
   1803               bool Enabled2 = std::is_convertible<OtherValueType*, ValueType*>::value,
   1804               typename Dummy = std::enable_if_t<Enabled1 && Enabled2>>
   1805     constexpr strided_span(multi_span<OtherValueType, Dimensions...> av, bounds_type bounds)
   1806         : strided_span(av.data(), av.bounds().total_size(), std::move(bounds))
   1807     {
   1808     }
   1809 
   1810     // convertible
   1811     template <typename OtherValueType, typename Dummy = std::enable_if_t<std::is_convertible<
   1812                                            OtherValueType (*)[], value_type (*)[]>::value>>
   1813     constexpr strided_span(const strided_span<OtherValueType, Rank>& other)
   1814         : data_(other.data_), bounds_(other.bounds_)
   1815     {
   1816     }
   1817 
   1818     // convert from bytes
   1819     template <typename OtherValueType>
   1820     constexpr strided_span<
   1821         typename std::enable_if<std::is_same<value_type, const byte>::value, OtherValueType>::type,
   1822         Rank>
   1823     as_strided_span() const
   1824     {
   1825         static_assert((sizeof(OtherValueType) >= sizeof(value_type)) &&
   1826                           (sizeof(OtherValueType) % sizeof(value_type) == 0),
   1827                       "OtherValueType should have a size to contain a multiple of ValueTypes");
   1828         auto d = narrow_cast<size_type>(sizeof(OtherValueType) / sizeof(value_type));
   1829 
   1830         size_type size = this->bounds().total_size() / d;
   1831         return {const_cast<OtherValueType*>(reinterpret_cast<const OtherValueType*>(this->data())),
   1832                 size, bounds_type{resize_extent(this->bounds().index_bounds(), d),
   1833                                   resize_stride(this->bounds().strides(), d)}};
   1834     }
   1835 
   1836     constexpr strided_span section(index_type origin, index_type extents) const
   1837     {
   1838         size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
   1839         return {&this->operator[](origin), size,
   1840                 bounds_type{extents, details::make_stride(bounds())}};
   1841     }
   1842 
   1843     constexpr reference operator[](const index_type& idx) const
   1844     {
   1845         return data_[bounds_.linearize(idx)];
   1846     }
   1847 
   1848     template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
   1849     constexpr Ret operator[](size_type idx) const
   1850     {
   1851         Expects(idx < bounds_.size()); // index is out of bounds of the array
   1852         const size_type ridx = idx * bounds_.stride();
   1853 
   1854         // index is out of bounds of the underlying data
   1855         Expects(ridx < bounds_.total_size());
   1856         return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()};
   1857     }
   1858 
   1859     constexpr bounds_type bounds() const noexcept { return bounds_; }
   1860 
   1861     template <size_t Dim = 0>
   1862     constexpr size_type extent() const noexcept
   1863     {
   1864         static_assert(Dim < Rank,
   1865                       "dimension should be less than Rank (dimension count starts from 0)");
   1866         return bounds_.template extent<Dim>();
   1867     }
   1868 
   1869     constexpr size_type size() const noexcept { return bounds_.size(); }
   1870 
   1871     constexpr pointer data() const noexcept { return data_; }
   1872 
   1873     constexpr explicit operator bool() const noexcept { return data_ != nullptr; }
   1874 
   1875     constexpr iterator begin() const { return iterator{this, true}; }
   1876 
   1877     constexpr iterator end() const { return iterator{this, false}; }
   1878 
   1879     constexpr const_iterator cbegin() const
   1880     {
   1881         return const_iterator{reinterpret_cast<const const_strided_span*>(this), true};
   1882     }
   1883 
   1884     constexpr const_iterator cend() const
   1885     {
   1886         return const_iterator{reinterpret_cast<const const_strided_span*>(this), false};
   1887     }
   1888 
   1889     constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; }
   1890 
   1891     constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; }
   1892 
   1893     constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; }
   1894 
   1895     constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; }
   1896 
   1897     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1898               typename Dummy = std::enable_if_t<std::is_same<
   1899                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1900     constexpr bool operator==(const strided_span<OtherValueType, OtherRank>& other) const noexcept
   1901     {
   1902         return bounds_.size() == other.bounds_.size() &&
   1903                (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
   1904     }
   1905 
   1906     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1907               typename Dummy = std::enable_if_t<std::is_same<
   1908                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1909     constexpr bool operator!=(const strided_span<OtherValueType, OtherRank>& other) const noexcept
   1910     {
   1911         return !(*this == other);
   1912     }
   1913 
   1914     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1915               typename Dummy = std::enable_if_t<std::is_same<
   1916                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1917     constexpr bool operator<(const strided_span<OtherValueType, OtherRank>& other) const noexcept
   1918     {
   1919         return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
   1920     }
   1921 
   1922     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1923               typename Dummy = std::enable_if_t<std::is_same<
   1924                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1925     constexpr bool operator<=(const strided_span<OtherValueType, OtherRank>& other) const noexcept
   1926     {
   1927         return !(other < *this);
   1928     }
   1929 
   1930     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1931               typename Dummy = std::enable_if_t<std::is_same<
   1932                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1933     constexpr bool operator>(const strided_span<OtherValueType, OtherRank>& other) const noexcept
   1934     {
   1935         return (other < *this);
   1936     }
   1937 
   1938     template <typename OtherValueType, std::ptrdiff_t OtherRank,
   1939               typename Dummy = std::enable_if_t<std::is_same<
   1940                   std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
   1941     constexpr bool operator>=(const strided_span<OtherValueType, OtherRank>& other) const noexcept
   1942     {
   1943         return !(*this < other);
   1944     }
   1945 
   1946 private:
   1947     static index_type resize_extent(const index_type& extent, std::ptrdiff_t d)
   1948     {
   1949         // The last dimension of the array needs to contain a multiple of new type elements
   1950         Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0));
   1951 
   1952         index_type ret = extent;
   1953         ret[Rank - 1] /= d;
   1954 
   1955         return ret;
   1956     }
   1957 
   1958     template <bool Enabled = (Rank == 1), typename Dummy = std::enable_if_t<Enabled>>
   1959     static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = 0)
   1960     {
   1961         // Only strided arrays with regular strides can be resized
   1962         Expects(strides[Rank - 1] == 1);
   1963 
   1964         return strides;
   1965     }
   1966 
   1967     template <bool Enabled = (Rank > 1), typename Dummy = std::enable_if_t<Enabled>>
   1968     static index_type resize_stride(const index_type& strides, std::ptrdiff_t d)
   1969     {
   1970         // Only strided arrays with regular strides can be resized
   1971         Expects(strides[Rank - 1] == 1);
   1972         // The strides must have contiguous chunks of
   1973         // memory that can contain a multiple of new type elements
   1974         Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0));
   1975 
   1976         for (size_t i = Rank - 1; i > 0; --i) {
   1977             // Only strided arrays with regular strides can be resized
   1978             Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0));
   1979         }
   1980 
   1981         index_type ret = strides / d;
   1982         ret[Rank - 1] = 1;
   1983 
   1984         return ret;
   1985     }
   1986 };
   1987 
   1988 template <class Span>
   1989 class contiguous_span_iterator
   1990     : public std::iterator<std::random_access_iterator_tag, typename Span::value_type>
   1991 {
   1992     using Base = std::iterator<std::random_access_iterator_tag, typename Span::value_type>;
   1993 
   1994 public:
   1995     using typename Base::reference;
   1996     using typename Base::pointer;
   1997     using typename Base::difference_type;
   1998 
   1999 private:
   2000     template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
   2001     friend class multi_span;
   2002 
   2003     pointer data_;
   2004     const Span* m_validator;
   2005     void validateThis() const
   2006     {
   2007         // iterator is out of range of the array
   2008         Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size());
   2009     }
   2010     contiguous_span_iterator(const Span* container, bool isbegin)
   2011         : data_(isbegin ? container->data_ : container->data_ + container->size())
   2012         , m_validator(container)
   2013     {
   2014     }
   2015 
   2016 public:
   2017     reference operator*() const noexcept
   2018     {
   2019         validateThis();
   2020         return *data_;
   2021     }
   2022     pointer operator->() const noexcept
   2023     {
   2024         validateThis();
   2025         return data_;
   2026     }
   2027     contiguous_span_iterator& operator++() noexcept
   2028     {
   2029         ++data_;
   2030         return *this;
   2031     }
   2032     contiguous_span_iterator operator++(int) noexcept
   2033     {
   2034         auto ret = *this;
   2035         ++(*this);
   2036         return ret;
   2037     }
   2038     contiguous_span_iterator& operator--() noexcept
   2039     {
   2040         --data_;
   2041         return *this;
   2042     }
   2043     contiguous_span_iterator operator--(int) noexcept
   2044     {
   2045         auto ret = *this;
   2046         --(*this);
   2047         return ret;
   2048     }
   2049     contiguous_span_iterator operator+(difference_type n) const noexcept
   2050     {
   2051         contiguous_span_iterator ret{*this};
   2052         return ret += n;
   2053     }
   2054     contiguous_span_iterator& operator+=(difference_type n) noexcept
   2055     {
   2056         data_ += n;
   2057         return *this;
   2058     }
   2059     contiguous_span_iterator operator-(difference_type n) const noexcept
   2060     {
   2061         contiguous_span_iterator ret{*this};
   2062         return ret -= n;
   2063     }
   2064     contiguous_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
   2065     difference_type operator-(const contiguous_span_iterator& rhs) const noexcept
   2066     {
   2067         Expects(m_validator == rhs.m_validator);
   2068         return data_ - rhs.data_;
   2069     }
   2070     reference operator[](difference_type n) const noexcept { return *(*this + n); }
   2071     bool operator==(const contiguous_span_iterator& rhs) const noexcept
   2072     {
   2073         Expects(m_validator == rhs.m_validator);
   2074         return data_ == rhs.data_;
   2075     }
   2076     bool operator!=(const contiguous_span_iterator& rhs) const noexcept { return !(*this == rhs); }
   2077     bool operator<(const contiguous_span_iterator& rhs) const noexcept
   2078     {
   2079         Expects(m_validator == rhs.m_validator);
   2080         return data_ < rhs.data_;
   2081     }
   2082     bool operator<=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs < *this); }
   2083     bool operator>(const contiguous_span_iterator& rhs) const noexcept { return rhs < *this; }
   2084     bool operator>=(const contiguous_span_iterator& rhs) const noexcept { return !(rhs > *this); }
   2085     void swap(contiguous_span_iterator& rhs) noexcept
   2086     {
   2087         std::swap(data_, rhs.data_);
   2088         std::swap(m_validator, rhs.m_validator);
   2089     }
   2090 };
   2091 
   2092 template <typename Span>
   2093 contiguous_span_iterator<Span> operator+(typename contiguous_span_iterator<Span>::difference_type n,
   2094                                          const contiguous_span_iterator<Span>& rhs) noexcept
   2095 {
   2096     return rhs + n;
   2097 }
   2098 
   2099 template <typename Span>
   2100 class general_span_iterator
   2101     : public std::iterator<std::random_access_iterator_tag, typename Span::value_type>
   2102 {
   2103     using Base = std::iterator<std::random_access_iterator_tag, typename Span::value_type>;
   2104 
   2105 public:
   2106     using typename Base::reference;
   2107     using typename Base::pointer;
   2108     using typename Base::difference_type;
   2109     using typename Base::value_type;
   2110 
   2111 private:
   2112     template <typename ValueType, size_t Rank>
   2113     friend class strided_span;
   2114 
   2115     const Span* m_container;
   2116     typename Span::bounds_type::iterator m_itr;
   2117     general_span_iterator(const Span* container, bool isbegin)
   2118         : m_container(container)
   2119         , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end())
   2120     {
   2121     }
   2122 
   2123 public:
   2124     reference operator*() noexcept { return (*m_container)[*m_itr]; }
   2125     pointer operator->() noexcept { return &(*m_container)[*m_itr]; }
   2126     general_span_iterator& operator++() noexcept
   2127     {
   2128         ++m_itr;
   2129         return *this;
   2130     }
   2131     general_span_iterator operator++(int) noexcept
   2132     {
   2133         auto ret = *this;
   2134         ++(*this);
   2135         return ret;
   2136     }
   2137     general_span_iterator& operator--() noexcept
   2138     {
   2139         --m_itr;
   2140         return *this;
   2141     }
   2142     general_span_iterator operator--(int) noexcept
   2143     {
   2144         auto ret = *this;
   2145         --(*this);
   2146         return ret;
   2147     }
   2148     general_span_iterator operator+(difference_type n) const noexcept
   2149     {
   2150         general_span_iterator ret{*this};
   2151         return ret += n;
   2152     }
   2153     general_span_iterator& operator+=(difference_type n) noexcept
   2154     {
   2155         m_itr += n;
   2156         return *this;
   2157     }
   2158     general_span_iterator operator-(difference_type n) const noexcept
   2159     {
   2160         general_span_iterator ret{*this};
   2161         return ret -= n;
   2162     }
   2163     general_span_iterator& operator-=(difference_type n) noexcept { return *this += -n; }
   2164     difference_type operator-(const general_span_iterator& rhs) const noexcept
   2165     {
   2166         Expects(m_container == rhs.m_container);
   2167         return m_itr - rhs.m_itr;
   2168     }
   2169     value_type operator[](difference_type n) const noexcept { return (*m_container)[m_itr[n]]; }
   2170 
   2171     bool operator==(const general_span_iterator& rhs) const noexcept
   2172     {
   2173         Expects(m_container == rhs.m_container);
   2174         return m_itr == rhs.m_itr;
   2175     }
   2176     bool operator!=(const general_span_iterator& rhs) const noexcept { return !(*this == rhs); }
   2177     bool operator<(const general_span_iterator& rhs) const noexcept
   2178     {
   2179         Expects(m_container == rhs.m_container);
   2180         return m_itr < rhs.m_itr;
   2181     }
   2182     bool operator<=(const general_span_iterator& rhs) const noexcept { return !(rhs < *this); }
   2183     bool operator>(const general_span_iterator& rhs) const noexcept { return rhs < *this; }
   2184     bool operator>=(const general_span_iterator& rhs) const noexcept { return !(rhs > *this); }
   2185     void swap(general_span_iterator& rhs) noexcept
   2186     {
   2187         std::swap(m_itr, rhs.m_itr);
   2188         std::swap(m_container, rhs.m_container);
   2189     }
   2190 };
   2191 
   2192 template <typename Span>
   2193 general_span_iterator<Span> operator+(typename general_span_iterator<Span>::difference_type n,
   2194                                       const general_span_iterator<Span>& rhs) noexcept
   2195 {
   2196     return rhs + n;
   2197 }
   2198 
   2199 } // namespace gsl
   2200 
   2201 #ifdef _MSC_VER
   2202 
   2203 #undef constexpr
   2204 #pragma pop_macro("constexpr")
   2205 
   2206 #if _MSC_VER <= 1800
   2207 #pragma warning(pop)
   2208 
   2209 #ifndef GSL_THROW_ON_CONTRACT_VIOLATION
   2210 #undef noexcept
   2211 #pragma pop_macro("noexcept")
   2212 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
   2213 
   2214 #undef GSL_MSVC_HAS_VARIADIC_CTOR_BUG
   2215 
   2216 #endif // _MSC_VER <= 1800
   2217 
   2218 #endif // _MSC_VER
   2219 
   2220 #if defined(GSL_THROW_ON_CONTRACT_VIOLATION)
   2221 
   2222 #undef noexcept
   2223 
   2224 #ifdef _MSC_VER
   2225 #pragma warning(pop)
   2226 #pragma pop_macro("noexcept")
   2227 #endif
   2228 
   2229 #endif // GSL_THROW_ON_CONTRACT_VIOLATION
   2230 
   2231 #endif // GSL_MULTI_SPAN_H
   2232