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