Multidimensional bounds, offset and array_view, revision 7

ISO/IEC JTC1 SC22 WG21 N4512 - 2015-05-07

Reply-to:
Łukasz Mendakiewicz <lukaszme@microsoft.com>
Herb Sutter <hsutter@microsoft.com>

Overview

Revision 7 (N4512) incorporates additional changes requested by LWG in Lenexa meeting, marked as deletions and insertions.

Revision 6 (N4494) incorporates the changes requested by LWG in Cologne meeting.

The following suggestions were implemented fully:

  1. Rephrased coord.general avoid references to mathematical entites.
  2. Renamed index to offset.
  3. Changed int Rank template parameter to size_t Rank throughout the document.
  4. Made offset, bounds and bounds_iterator binary operators (apart from @= forms) free functions.
  5. Replaced term "component" with "element" when referring to the individual constituents of offset or bounds.
  6. In coord.bounds.require replaced prose with an equivalent mathematical expression.
  7. In coord.bounds.iterator and coord.bounds.iterator.require removed the requirement on bounds_iterator to represent a random access iterator, replacing with "as- if" phrasing.
  8. In the description of bounds_iterator& operator++() replaced the code snippet with equivalent prose.
  9. In views.general changed the font back to non-monospace.
  10. Removed views.require, duplicating it as arrayview.require and stridedarrayview.require.
  11. Removed redundant assignment operators on array_view and strided_array_view.
  12. Employed "exposition only" data members is the descriptions of array_view and strided_array_view semantics.
  13. Rephrased the first paragraph in arrayview.cons to avoid ambiguity in binding of the token "type".
  14. In constexpr array_view(Viewable&& vw) rephrased the third bullet point.

The following suggestion was implemented partially:

  1. Instead of the array_view(ArrayType& arr) constructor being completely removed, it has been constrained to 1-D case as the Committee indicated that such case does not exhibit the undefined behavior. We believe that the request to remove it completely was a misstatment.

The following suggestion was not implemented:

  1. The semantics of the proposed types were not extended to allow rank-0 cases. We feel that we lack sufficient practical experience in using such cases and we are afraid of some contention points when it comes to defining their detailed semantics. We observe that such an extension can be introduced in future without conflicting with the proposal in the current form.

Revision 5 (N4346) incorporates the changes requested by LWG in Urbana-Champaign meeting.

Revision 4 (N4177) contains the following changes:

  1. The order of some of the array_view and strided_array_view constructor parameters have been switched from {size, location} to {location, size} for consistency with the existing practice in STL (vide copy_n).
  2. Alias templates for constant views have been introduced.

Revision 3 (N4087) incorporates the feedback received in Rapperswil from LEWG and some other minor fixes:

  1. Fixed specification of initializer_list constructors in index and bounds — the size of the initializer_list cannot always be verified at the compile-time, so the check must be expressed as "Requires" instead of SFINAE.
  2. Improved the wording for operator- and operator-= in index and bounds to avoid negating rhs.
  3. Added (const_)iterator type aliases in bounds.
  4. Slightly improved the wording in the array_view(bounds_type bounds, pointer ptr) specification.
  5. Slightly improved the wording in the bounds_iterator::operator++ specification.
  6. Tightened the specification of views to prevent derived-to-base conversions.
  7. Made array_view(Viewable&&) and array_view(ArrayType&) constructors not "explicit".
  8. Fixed array_view(bounds_type bounds, pointer ptr) constructor not to be "noexcept".
  9. Provided a definition for "uniformly strided".
  10. Renamed "generalized views" to "views".
  11. Changed index and bounds arithmetic operators from accepting any ArithmeticType to only value_type/ptrdiff_t.
  12. Improved the overload resolution for array_view constructors so that all conversions between related array_views can be noexcept.
  13. Slightly improved consistency and mistakes between "this constructor" and "this function" wording.

Acknowledgements

Thanks to Stephan T. Lavavej, Matthew Fioravante, Robert Kawulak and the members of LEWG for the suggested improvements. Thanks to the interlocutors at ISO C++ Standard - Future Proposals forum for the valuable feedback. Thanks to all correspondents expressing feedback in private emails.

Wording changes

The proposed wording changes are relative to the contents of N3936.

17.6.1.2 Headers [headers]

Edit within paragraph 2 as follows.

The C++ standard library provides 55 C++ library headers, as shown in Table 14.

Add the following items to table 14.

<array_view>
<coordinate>

Chapter 20 General utilities library [utilities]

Add a row to table 44 as follows.

Table 44: General utilities library summary
SubclauseHeader(s)
20.2 Utility components <utility>
20.3 Pairs <utility>
20.4 Tuples <tuple>
20.5 Compile-time integer sequences <utility>
20.6 Multidimensional coordinates <coordinate>
20.7 Fixed-size sequences of bits <bitset>
<memory>
20.8 Memory <cstdlib>
<cstring>
20.9 Smart pointers <memory>
20.10 Function objects <functional>
20.11 Type traits <type_traits>
20.12 Compile-time rational arithmetic <ratio>
20.13 Time utilities <chrono>
<ctime>
20.14 Scoped allocators <scoped_allocator>
20.15 Type indexes <typeindex>

20.6 Multidimensional coordinates [coord]

Add a new section after the intseq section.

20.6.1 In general [coord.general]

Add a new section:

This subclause describes the multidimensional coordinates library. It provides a class template offset which is an N-tuple of coordinates representing locations and offsets in N-dimensional data structures, a class template bounds which is an N-tuple representing extents of such data structures, and a class template bounds_iterator which allows iteration over a space defined by such extents.

Add a new synopsis:

Header <experimental/coordinate> synopsis


#include <initializer_list>

namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {

    // [coord.offset], class template offset
    template <size_t Rank> class offset;

    // [coord.offset.eq], offset equality
    template <size_t Rank>
      constexpr bool operator==(const offset<Rank>& lhs, const offset<Rank>& rhs) noexcept;
    template <size_t Rank>
      constexpr bool operator!=(const offset<Rank>& lhs, const offset<Rank>& rhs) noexcept;

    // [coord.offset.arith], offset arithmetic
    template <size_t Rank>
      constexpr offset<Rank> operator+(const offset<Rank>& lhs, const offset<Rank>& rhs);
    template <size_t Rank>
      constexpr offset<Rank> operator-(const offset<Rank>& lhs, const offset<Rank>& rhs);
    template <size_t Rank>
      constexpr offset<Rank> operator*(const offset<Rank>& lhs, ptrdiff_t v);
    template <size_t Rank>
      constexpr offset<Rank> operator*(ptrdiff_t v, const offset<Rank>& rhs);
    template <size_t Rank>
      constexpr offset<Rank> operator/(const offset<Rank>& lhs, ptrdiff_t v);

    // [coord.bounds], class template bounds
    template <size_t Rank> class bounds;

    // [coord.bounds.eq], bounds equality
    template <size_t Rank>
      constexpr bool operator==(const bounds<Rank>& lhs, const bounds<Rank>& rhs) noexcept;
    template <size_t Rank>
      constexpr bool operator!=(const bounds<Rank>& lhs, const bounds<Rank>& rhs) noexcept;

    // [coord.bounds.arith], bounds arithmetic
    template <size_t Rank>
      constexpr bounds<Rank> operator+(const bounds<Rank>& lhs, const offset<Rank>& rhs);
    template <size_t Rank>
      constexpr bounds<Rank> operator+(const offset<Rank>& lhs, const bounds<Rank>& rhs);
    template <size_t Rank>
      constexpr bounds<Rank> operator-(const bounds<Rank>& lhs, const offset<Rank>& rhs);
    template <size_t Rank>
      constexpr bounds<Rank> operator*(const bounds<Rank>& lhs, ptrdiff_t v);
    template <size_t Rank>
      constexpr bounds<Rank> operator*(ptrdiff_t v, const bounds<Rank>& rhs);
    template <size_t Rank>
      constexpr bounds<Rank> operator/(const bounds<Rank>& lhs, ptrdiff_t v);

    // [coord.bounds.iterator], class template bounds_iterator
    template <size_t Rank> class bounds_iterator;

    template <size_t Rank>
      bool                  operator==(const bounds_iterator<Rank>& lhs,
                                       const bounds_iterator<Rank>& rhs);
    template <size_t Rank>
      bool                  operator!=(const bounds_iterator<Rank>& lhs,
                                       const bounds_iterator<Rank>& rhs);
    template <size_t Rank>
      bool                  operator<(const bounds_iterator<Rank>& lhs,
                                      const bounds_iterator<Rank>& rhs);
    template <size_t Rank>
      bool                  operator<=(const bounds_iterator<Rank>& lhs,
                                       const bounds_iterator<Rank>& rhs);
    template <size_t Rank>
      bool                  operator>(const bounds_iterator<Rank>& lhs,
                                      const bounds_iterator<Rank>& rhs);
    template <size_t Rank>
      bool                  operator>=(const bounds_iterator<Rank>& lhs,
                                       const bounds_iterator<Rank>& rhs);
    template <size_t Rank>
      bounds_iterator<Rank> operator+(typename bounds_iterator<Rank>::difference_type n,
                                      const bounds_iterator<Rank>& rhs);
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

20.6.2 Class template offset [coord.offset]

Add a new section:


namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {
    template <size_t Rank>
    class offset {
    public:
      // constants and types
      static constexpr size_t rank = Rank;
      using reference           = ptrdiff_t&;
      using const_reference     = const ptrdiff_t&;
      using size_type           = size_t;
      using value_type          = ptrdiff_t;

      // [coord.offset.cnstr], offset construction
      constexpr offset() noexcept;
      constexpr offset(value_type v) noexcept;           // only if Rank == 1
      constexpr offset(initializer_list<value_type> il);

      // [coord.offset.cmptelem], offset element access
      constexpr reference       operator[](size_type n);
      constexpr const_reference operator[](size_type n) const;

      // [coord.offset.arith], offset arithmetic
      
      constexpr offset& operator+=(const offset& rhs);
      constexpr offset& operator-=(const offset& rhs);

      constexpr offset& operator++();    // only if Rank == 1
      constexpr offset  operator++(int); // only if Rank == 1
      constexpr offset& operator--();    // only if Rank == 1
      constexpr offset  operator--(int); // only if Rank == 1

      constexpr offset  operator+() const noexcept;
      constexpr offset  operator-() const;

      
      constexpr offset& operator*=(value_type v);
      constexpr offset& operator/=(value_type v);
    };
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

20.6.2.1 General requirements [coord.offset.require]

Add a new section:

If Rank is less than 1 the program is ill-formed.

20.6.2.2 Construction [coord.offset.cnstr]

Add a new section:

constexpr offset() noexcept;

Effects: Zero-initializes each element.

constexpr offset(value_type v) noexcept;

Effects: Initializes the 0th element of *this with v.

Remarks: This constructor shall not participate in overload resolution unless Rank is 1.

constexpr offset(initializer_list<value_type> il);

Requires: il.size() == Rank.

Effects: For all i in the range [0, Rank), initializes the ith element of *this with *(il.begin() + i).

20.6.2.3 Equality [coord.offset.eq]

Add a new section:

template <size_t Rank>
  constexpr bool operator==(const offset<Rank>& lhs, const offset<Rank>& rhs) noexcept;

Returns: true if lhs[i] == rhs[i] for all i in the range [0, Rank), otherwise false.

template <size_t Rank>
  constexpr bool operator!=(const offset<Rank>& lhs, const offset<Rank>& rhs) noexcept;

Returns: !(lhs == rhs).

20.6.2.4 Element access [coord.offset.cmptelem]

Add a new section:

constexpr reference operator[](size_type n);
constexpr const_reference operator[](size_type n) const;

Requires: n < Rank.

Returns: A reference to the nth element of *this.

20.6.2.5 Arithmetic [coord.offset.arith]

Add a new section:

constexpr offset& operator+=(const offset& rhs);

Effects: For all i in the range [0, Rank), adds the ith element of rhs to the ith element of *this and stores the sum in the ith element of *this.

Returns: *this.

constexpr offset& operator-=(const offset& rhs);

Effects: For all i in the range [0, Rank), subtracts the ith element of rhs from the ith element of *this and stores the difference in the ith element of *this.

Returns: *this.

constexpr offset& operator++();

Effects: ++(*this)[0].

Returns: *this.

Remarks: This function shall not participate in overload resolution unless Rank == 1.

constexpr offset operator++(int);

Returns: offset<Rank>{(*this)[0]++}.

Remarks: This function shall not participate in overload resolution unless Rank == 1.

constexpr offset& operator--();

Effects: --(*this)[0].

Returns: *this.

Remarks: This function shall not participate in overload resolution unless Rank == 1.

constexpr offset operator--(int);

Returns: offset<Rank>{(*this)[0]--}.

Remarks: This function shall not participate in overload resolution unless Rank == 1.

constexpr offset operator+() const noexcept;

Returns: *this.

constexpr offset operator-() const;

Returns: A copy of *this with each element negated.

constexpr offset& operator*=(value_type v);

Effects: For all i in the range [0, Rank), multiplies the ith element of *this by v and stores the product in the ith element of *this.

Returns: *this.

constexpr offset& operator/=(value_type v);

Effects: For all i in the range [0, Rank), divides the ith element of *this by v and stores the quotient in the ith element of *this.

Returns: *this.

template <size_t Rank>
  constexpr offset<Rank> operator+(const offset<Rank>& lhs, const offset<Rank>& rhs);

Returns: offset<Rank>{lhs} += rhs.

template <size_t Rank>
  constexpr offset<Rank> operator-(const offset<Rank>& lhs, const offset<Rank>& rhs);

Returns: offset<Rank>{lhs} -= rhs.

template <size_t Rank>
  constexpr offset<Rank> operator*(const offset<Rank>& lhs, ptrdiff_t v);

Returns: offset<Rank>{lhs} *= v.

template <size_t Rank>
  constexpr offset<Rank> operator*(ptrdiff_t v, const offset<Rank>& rhs);

Returns: offset<Rank>{rhs} *= v.

template <size_t Rank>
  constexpr offset<Rank> operator/(const offset<Rank>& lhs, ptrdiff_t v);

Returns: offset<Rank>{lhs} /= v.

20.6.3 Class template bounds [coord.bounds]

Add a new section:


namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {

    template <size_t Rank>
    class bounds {
    public:
      // constants and types
      static constexpr size_t rank = Rank;
      using reference           = ptrdiff_t&;
      using const_reference     = const ptrdiff_t&;
      using iterator            = bounds_iterator<Rank>;
      using const_iterator      = bounds_iterator<Rank>;
      using size_type           = size_t;
      using value_type          = ptrdiff_t;

      // [coord.bounds.cnstr], bounds construction
      constexpr bounds() noexcept;
      constexpr bounds(value_type v);                    // only if Rank == 1
      constexpr bounds(initializer_list<value_type> il);

      

      // [coord.bounds.obs], bounds observers
      constexpr size_type size() const noexcept;
      constexpr bool      contains(const offset<Rank>& idx) const noexcept;

      // [coord.bounds.iter], bounds iterators
      const_iterator begin() const noexcept;
      const_iterator end() const noexcept;

      // [coord.bounds.cmptelem], bounds element access
      constexpr reference       operator[](size_type n);
      constexpr const_reference operator[](size_type n) const;

      // [coord.bounds.arith], bounds arithmetic
      
      constexpr bounds& operator+=(const offset<Rank>& rhs);
      constexpr bounds& operator-=(const offset<Rank>& rhs);

      
      constexpr bounds& operator*=(value_type v);
      constexpr bounds& operator/=(value_type v);
    };
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

20.6.3.1 General requirements [coord.bounds.require]

Add a new section:

If Rank is less than 1 the program is ill-formed.

Construction of and every mutating operation on an object b of type bounds shall leave the object in a state that satisfies the following constraints:

  1. b[i] >= 0 for all i in the range [0, Rank).
  2. b[0] × b[1] × ... × b[Rank - 1] <= numeric_limits<ptrdiff_t>::max().

Otherwise, the behavior is undefined.

20.6.3.2 Construction [coord.bounds.cnstr]

Add a new section:

constexpr bounds() noexcept;

Effects: Zero-initializes each element.

constexpr bounds(value_type v);

Effects: Initializes the 0th element of *this with v.

Remarks: This constructor shall not participate in overload resolution unless Rank is 1.

constexpr bounds(initializer_list<value_type> il);

Requires: il.size() == Rank.

Effects: For all i in the range [0, Rank), initializes the ith element of *this with *(il.begin() + i).

20.6.3.3 Equality [coord.bounds.eq]

Add a new section:

template <size_t Rank>
  constexpr bool operator==(const bounds<Rank>& lhs, const bounds<Rank>& rhs) noexcept;

Returns: true if lhs[i] == rhs[i] for all i in the range [0, Rank), otherwise false.

template <size_t Rank>
  constexpr bool operator!=(const bounds<Rank>& lhs, const bounds<Rank>& rhs) noexcept;

Returns: !(lhs == rhs).

20.6.3.4 Observers [coord.bounds.obs]

Add a new section:

constexpr size_type size() const noexcept;

Returns: The product of all elements of *this.

constexpr bool contains(const offset<Rank>& idx) const noexcept;

Returns: true if 0 <= idx[i] and idx[i] < (*this)[i] for all i in the range [0, Rank), otherwise false.

20.6.3.5 Iterators [coord.bounds.iter]

Add a new section:

bounds_iterator<Rank>const_iterator begin() const noexcept;

Returns: A bounds_iterator referring to the first element of the space defined by *this such that *begin() == offset<Rank>{} if size() != 0,otherwise begin() == end() otherwise.

bounds_iterator<Rank>const_iterator end() const noexcept;

Returns: A bounds_iterator which is the past-the-end iterator for the space defined by *this.

20.6.3.6 Element access [coord.bounds.cmptelem]

Add a new section:

constexpr reference operator[](size_type n);
constexpr const_reference operator[](size_type n) const;

Requires: n < Rank.

Returns: A reference to the nth element of *this.

20.6.3.7 Arithmetic [coord.bounds.arith]

Add a new section:

constexpr bounds& operator+=(const offset<Rank>& rhs);

Effects: For all i in the range [0, Rank), adds the ith element of rhs to the ith element of *this and stores the sum in the ith element of *this.

Returns: *this.

constexpr bounds& operator-=(const offset<Rank>& rhs);

Effects: For all i in the range [0, Rank), subtracts the ith element of rhs from the ith element of *this and stores the difference in the ith element of *this.

Returns: *this.

constexpr bounds& operator*=(value_type v);

Effects: For all i in the range [0, Rank), multiplies the ith element of *this by v and stores the product in the ith element of *this.

Returns: *this.

constexpr bounds& operator/=(value_type v);

Effects: For all i in the range [0, Rank), divides the ith element of *this by v and stores the quotient in the ith element of *this.

Returns: *this.

template <size_t Rank>
  constexpr bounds<Rank> operator+(const bounds<Rank>& lhs, const offset<Rank>& rhs);

Returns: bounds<Rank>{lhs} += rhs.

template <size_t Rank>
  constexpr bounds<Rank> operator+(const offset<Rank>& lhs, const bounds<Rank>& rhs);

Returns: bounds<Rank>{rhs} += lhs.

template <size_t Rank>
  constexpr bounds<Rank> operator-(const bounds<Rank>& lhs, const offset<Rank>& rhs);

Returns: bounds<Rank>{lhs} -= rhs.

template <size_t Rank>
  constexpr bounds<Rank> operator*(const bounds<Rank>& lhs, ptrdiff_t v);

Returns: bounds<Rank>{lhs} *= v.

template <size_t Rank>
  constexpr bounds<Rank> operator*(ptrdiff_t v, const bounds<Rank>& rhs);

Returns: bounds<Rank>{rhs} *= v.

template <size_t Rank>
  constexpr bounds<Rank> operator/(const bounds<Rank>& lhs, ptrdiff_t v);

Returns: bounds<Rank>{lhs} /= v.

20.6.4 Class template bounds_iterator [coord.bounds.iterator]

Add a new section:

SemantincsSemantics of the bounds_iterator shall follow the semantics of a random access iterator ([random.access.iterators]) unless otherwise specified below.


namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {

    template <size_t Rank>
    class bounds_iterator
    {
    public:
      using iterator_category = unspecified;
      using value_type        = offset<Rank>;
      using difference_type   = ptrdiff_t;
      using pointer           = unspecified;   // See [coord.bounds.iterator.require]
      using reference         = const offset<Rank>;

      

      bounds_iterator& operator++();
      bounds_iterator  operator++(int);
      bounds_iterator& operator--();
      bounds_iterator  operator--(int);

      bounds_iterator  operator+(difference_type n) const;
      bounds_iterator& operator+=(difference_type n);
      bounds_iterator  operator-(difference_type n) const;
      bounds_iterator& operator-=(difference_type n);

      difference_type  operator-(const bounds_iterator& rhs) const;

      reference operator*() const;
      pointer   operator->() const;
      reference operator[](difference_type n) const;

    private:
      bounds<Rank> bnd_;  // exposition only
      offset<Rank>  idx_;  // exposition only
    };
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

20.6.4.1 General requirements [coord.bounds.iterator.require]

Add a new section:

If Rank is less than 1 the program is ill-formed.

pointer shall be an unspecified type such that for a bounds_iterator it the expression it->E is equivalent to (*it).E and that for an object p of type pointer the expression p->E yields the same result irrespective of whether the state of the bounds_iterator object has changed or its lifetime has ended.

[Note: All functions in the library that take a pair of iterators to denote a range shall treat bounds_iterator iterators as- if they were random access iterators, even though the pointer type is not a true pointer and the reference type is not a true reference. —end note]

20.6.4.2 Functions [coord.bounds.iterator.func]

Add a new section:

bool operator==(const bounds_iterator& rhs) const;

Requires: *this and rhs are iterators over the same bounds object.

Returns: idx_ == rhs.idx_.

bounds_iterator& operator++();

Requires: *this is not the past-the-end iterator.

Effects:

Increments idx_[Rank - 1]. If idx_[Rank - 1] is equal to bnd_[Rank - 1], sets idx_[Rank - 1] to zero and repeats the process with Rank - 2, and so on, until idx_[0] is equal to bnd_[0], at which points sets idx_ to an unspecified past-the-end value.

[Example: Given bounds_iterator<2> with bnd_ == {3, 2} and idx_ == {0, 0}, subsequent calls to operator++ will result in idx_ being equal to: {0, 1}, {1, 0}, {1, 1}, {2, 0}, {2, 1}, unspecified past-the-end value. —end example]

Returns: *this.

bounds_iterator& operator--();

Requires: There exists a bounds_iterator<Rank> it such that *this == ++it.

Effects: *this = it.

Returns: *this.

reference operator*() const;

Requires: *this is not the past-the-end iterator.

Returns: idx_.

Chapter 23 Containers library [containers]

Edit within paragraph 2 as follows.

The following subclauses describe container requirements, components for sequence containers and associative containers, and views, as summarized in Table 95.

Add a row to table 95 as follows.

Table 95: Containers library summary
SubclauseHeader(s)
23.2 Requirements
23.3 Sequence containers <array>
<deque>
<forward_list>
<list>
<vector>
23.4 Associative containers <map>
<set>
23.5 Unordered associative containers <unordered_map>
<unordered_set>
23.6 Container adaptors <queue>
<stack>
23.7 Views <array_view>

23.7 Views [views]

Add a new section after the container.adaptors section.

23.7.1 In general [views.general]

Add a new section:

The header <experimental/array_view> defines the views array_view and strided_array_view.

The objects in any valid range [ptr, ptr + size) are uniformly strided for a specific N-dimensional logical representation V parameterized by an N-dimensional vector stride if for every element in V the mapping between the location in V expressed as an N-dimensional vector idx and the address of the corresponding object in [ptr, ptr + size) can be computed as: ptr + idx · stride.

An array_view is a potentially multidimensional view on a sequence of uniformly strided objects of a uniform type, contiguous in the least significant dimension.

A strided_array_view is a potentially multidimensional view on a sequence of uniformly strided objects of a uniform type.

Add a new synopsis:

Header <experimental/array_view> synopsis


namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {

    // [arrayview], class template array_view
    template <class T, size_t Rank = 1> class array_view;

    // [stridedarrayview], class template strided_array_view
    template <class T, size_t Rank = 1> class strided_array_view;
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

23.7.2 Class template array_view [arrayview]

Add a new section:


namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {

    template <class T, size_t Rank = 1>
    class array_view {
    public:
      // constants and types
      static constexpr size_t rank = Rank;
      using offset_type          = offset<Rank>;
      using bounds_type         = bounds<Rank>;
      using size_type           = size_t;
      using value_type          = T;
      using pointer             = T*;
      using reference           = T&;

      // [arrayview.cons], array_view constructors, copy, and assignment
      constexpr array_view() noexcept;

      template <class Viewable>
        constexpr array_view(Viewable&& vw);                              // only if Rank == 1
      template <class U, size_t AnyRank>
        constexpr array_view(const array_view<U, AnyRank>& rhs) noexcept; // only if Rank == 1
      template <size_t Extent>
        constexpr array_view(value_type (&arr)[Extent]) noexcept;  // only if Rank == 1

      template <class U>
        constexpr array_view(const array_view<U, Rank>& rhs) noexcept;

      template <class Viewable>
        constexpr array_view(Viewable&& vw, bounds_type bounds);
      constexpr array_view(pointer ptr, bounds_type bounds);

      // [arrayview.obs], array_view observers
      constexpr bounds_type bounds() const noexcept;
      constexpr size_type   size() const noexcept;
      constexpr offset_type  stride() const noexcept;
      constexpr pointer     data() const noexcept;

      // [arrayview.elem], array_view element access
      constexpr reference operator[](const offset_type& idx) const;

      // [arrayview.subview], array_view slicing and sectioning
      constexpr array_view<T, Rank - 1>
        operator[](ptrdiff_t slice) const;      // only if Rank > 1
      constexpr strided_array_view<T, Rank>
        section(const offset_type& origin, const bounds_type& section_bnd) const;
      constexpr strided_array_view<T, Rank>
        section(const offset_type& origin) const;

    private:
      pointer     data_;    // exposition only
      bounds_type bounds_;  // exposition only
    };
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

23.7.2.1 array_view requirements [arrayview.require]

Add a new section:

T shall be an non-array object type. [Note: The type can be cv-qualified, resulting in semantics similar to the semantics of a pointer to cv-qualified type. —end note]

If Rank is less than 1 the program is ill-formed.

Any operation that invalidates a pointer in the range on which a view was created invalidates pointers and references returned from the view's functions.

Define VIEW_ACCESS(data, idx, stride, rank) as *(data + offset) where [Editorial note: The following expression should be formatted as LaTeX code —end note] offset = \sum_{i=0}^{rank - 1} idx_i \times stride_i, idxi = idx[i], and stridei = stride[i].

23.7.2.2 array_view constructors, copy, and assignment [arrayview.cons]

Add a new section:

For the purpose of this subclause, Viewable on U is a type satisfying the requirements set out in Table 104. In these definitions, let v denote an expression which type , type of which is Viewable on U.

Table 104: Viewable on U requirements
ExpressionReturn typeOperational semantics
v.size() Convertible to ptrdiff_t
v.data() Type T* such that T* is implicitly convertible to U*, and is_same_v<remove_cv_t<T>, remove_cv_t<U>> is true. static_cast<U*>(v.data()) points to a contiguous sequence of at least v.size() objects of (possibly cv-qualified) type remove_cv_t<U>.

[Example: The type vector<int> ([vector]) meets the requirements of all of the following: Viewable on int, Viewable on const int, Viewable on volatile int, and Viewable on const volatile int. —end example]

constexpr array_view() noexcept;

Postconditions: data_ == nullptr and bounds_.size() == 0.

template <class Viewable>
  constexpr array_view(Viewable&& vw);

Requires: vw shall satisfy the requirements of Viewable on value_type.

Postconditions: data_ == vw.data() and bounds_.size() == vw.size().

Remarks: This constructor shall not participate in overload resolution unless:

[Note: This provision ensures that either the following or the implicit copy constructor — both of which are noexcept — will be selected by overload resolution instead. —end note]

template <class U, size_t AnyRank>
  constexpr array_view(const array_view<U, AnyRank>& rhs) noexcept;

Postconditions: data_ == rhs.data() and bounds_.size() == rhs.size().

Remarks: This constructor shall not participate in overload resolution unless:

template <size_t Extent>
  constexpr array_view(value_type (&arr)[Extent]) noexcept;

Postconditions: data_ == arr and bounds_.size() == Extent.

Remarks: This constructor shall not participate in overload resolution unless Rank is 1.

template <class U>
  constexpr array_view(const array_view<U, Rank>& rhs) noexcept;

Postconditions: data_ == rhs.data() and bounds_ == rhs.bounds().

Remarks: This constructor shall not participate in overload resolution unless is_convertible_v<add_pointer_t<U>, pointer> is true and is_same_v<remove_cv_t<U>, remove_cv_t<value_type>> is true.

template <class Viewable>
  constexpr array_view(Viewable&& vw, bounds_type bounds);

Requires: bounds.size() <= vw.size().

Postconditions: data_ == vw.data() and bounds_ == bounds.

Remarks: This constructor shall not participate in overload resolution unless Viewable satisfies the syntactic requirements set in Table 104 for Viewable on value_type.

[Note: This constructor may be used to create an array_view with a different rank and/or bounds than the original array_view, i.e. reshape the view. —end note]

constexpr array_view(pointer ptr, bounds_type bounds);

Requires: [ptr, ptr + bounds.size()) is a valid range.

Postconditions: data_ == ptr and bounds_ == bounds.

23.7.2.3 array_view observers [arrayview.obs]

Add a new section:

constexpr bounds_type bounds() const noexcept;

Returns: bounds_.

constexpr size_type size() const noexcept;

Returns: bounds().size().

constexpr offset_type stride() const noexcept;

Returns: A value s such that:

constexpr pointer data() const noexcept;

Returns: data_.

23.7.2.4 array_view element access [arrayview.elem]

Add a new section:

constexpr reference operator[](const offset_type& idx) const;

Requires: bounds().contains(idx) == true.

Returns: VIEW_ACCESS(data(), idx, stride(), Rank).

23.7.2.5 array_view slicing and sectioning [arrayview.subview]

Add a new section:

constexpr array_view<T, Rank - 1>
  operator[](ptrdiff_t slice) const;

Requires: 0 <= slice and slice < bounds()[0].

Returns: A view vw such that vw.data_ is (*this)[{slice, 0, 0, ..., 0}], and vw.bounds_ is {bounds()[1], bounds()[2], ..., bounds()[Rank - 1]}.

Remarks: This function shall not participate in overload resolution unless Rank > 1.

constexpr strided_array_view<T, Rank>
  section(const offset_type& origin, const bounds_type& section_bnd) const;

Requires: bounds().contains(origin + idx) == true for any offset_type idx such that section_bnd.contains(idx) == true.

Returns: A strided view vw such that vw.data_ is (*this)[origin], vw.stride_ is stride(), and vw.bounds_ is section_bnd.

constexpr strided_array_view<T, Rank>
  section(const offset_type& origin) const;

Requires: bounds().contains(origin + idx) == true for any offset_type idx such that (bounds() - origin).contains(idx) == true.

Returns: A strided view vw such that vw.data_ is (*this)[origin], vw.stride_ is stride(), and vw.bounds_ is bounds() - origin.

23.7.3 Class template strided_array_view [stridedarrayview]

Add a new section:


namespace std {
  namespace experimental {
  inline namespace fundamentals_v2 {

    template <class T, size_t Rank = 1>
    class strided_array_view {
    public:
      // constants and types
      static constexpr size_t rank = Rank;
      using offset_type          = offset<Rank>;
      using bounds_type         = bounds<Rank>;
      using size_type           = size_t;
      using value_type          = T;
      using pointer             = T*;
      using reference           = T&;

      // [stridedarrayview.cons], strided_array_view constructors, copy, and assignment
      constexpr strided_array_view() noexcept;

      template <class U>
        constexpr strided_array_view(const array_view<U, Rank>& rhs) noexcept;
      template <class U>
        constexpr strided_array_view(const strided_array_view<U, Rank>& rhs) noexcept;

      constexpr strided_array_view(pointer ptr, bounds_type bounds, offset_type stride);

      // [stridedarrayview.obs], strided_array_view observers
      constexpr bounds_type bounds() const noexcept;
      constexpr size_type   size() const noexcept;
      constexpr offset_type  stride() const noexcept;

      // [stridedarrayview.elem], strided_array_view element access
      constexpr reference operator[](const offset_type& idx) const;

      // [stridedarrayview.subview], strided_array_view slicing and sectioning
      constexpr strided_array_view<T, Rank - 1>
        operator[](ptrdiff_t slice) const;      // only if Rank > 1
      constexpr strided_array_view<T, Rank>
        section(const offset_type& origin, const bounds_type& section_bnd) const;
      constexpr strided_array_view<T, Rank>
        section(const offset_type& origin) const;

    private:
      pointer     data_;    // exposition only
      bounds_type bounds_;  // exposition only
      offset_type stride_;  // exposition only
    };
  
  } // inline namespace fundamentals_v2
  } // namespace experimental
} // namespace std

23.7.3.1 strided_array_view requirements [stridedarrayview.require]

Add a new section:

T shall be an non-array object type. [Note: The type can be cv-qualified, resulting in semantics similar to the semantics of a pointer to cv-qualified type. —end note]

If Rank is less than 1 the program is ill-formed.

Any operation that invalidates a pointer in the range on which a view was created invalidates pointers and references returned from the view's functions.

Define VIEW_ACCESS(data, idx, stride, rank) as *(data + offset) where [Editorial note: The following expression should be formatted as LaTeX code —end note] offset = \sum_{i=0}^{rank - 1} idx_i \times stride_i, idxi = idx[i], and stridei = stride[i].

23.7.3.2 strided_array_view constructors, copy, and assignment [stridedarrayview.cons]

Add a new section:

constexpr strided_array_view() noexcept;

Postconditions: data_ == nullptr, bounds_.size() == 0, and stride_ == offset_type{}.

template <class U>
  constexpr strided_array_view(const array_view<U, Rank>& rhs) noexcept;
template <class U>
  constexpr strided_array_view(const strided_array_view<U, Rank>& rhs) noexcept;

Postconditions: For both constructors, bounds_ == rhs.bounds() and stride_ == rhs.stride(). For the first constructor, data_ == rhs.data(). For the second constructor, data_ == rhs.data_.

Remarks: These constructors shall not participate in overload resolution unless is_convertible_v<add_pointer_t<U>, pointer> is true and is_same_v<remove_cv_t<U>, remove_cv_t<value_type>> is true.

constexpr strided_array_view(pointer ptr, bounds_type bounds, offset_type stride);

Requires: For any offset_type idx such that bounds.contains(idx):

Postconditions: data_ == ptr, bounds_ == bounds, and stride_ == stride.

23.7.3.3 strided_array_view observers [stridedarrayview.obs]

Add a new section:

constexpr bounds_type bounds() const noexcept;

Returns: bounds_.

constexpr size_type size() const noexcept;

Returns: bounds().size().

constexpr offset_type stride() const noexcept;

Returns: stride_.

23.7.3.4 strided_array_view element access [stridedarrayview.elem]

Add a new section:

constexpr reference operator[](const offset_type& idx) const;

Requires: bounds().contains(idx) == true.

Returns: VIEW_ACCESS(data_, idx, stride(), Rank).

23.7.3.5 strided_array_view slicing and sectioning [stridedarrayview.subview]

Add a new section:

constexpr strided_array_view<T, Rank - 1>
  operator[](ptrdiff_t slice) const;

Requires: 0 <= slice and slice < bounds()[0].

Returns: A strided view vw such that vw.data_ is (*this)[{slice, 0, 0, ..., 0}], vw.bounds_ is {bounds()[1], bounds()[2], ..., bounds()[Rank - 1]}, and vw.stride_ is {stride()[1], stride()[2], ..., stride()[Rank - 1]}.

Remarks: This function shall not participate in overload resolution unless Rank > 1.

constexpr strided_array_view<T, Rank>
  section(const offset_type& origin, const bounds_type& section_bnd) const;

Requires: bounds().contains(origin + idx) == true for any offset_type idx such that section_bnd.contains(idx) == true.

Returns: A strided view vw such that vw.data_ is (*this)[origin], vw.stride_ is stride(), and vw.bounds_ is section_bnd.

constexpr strided_array_view<T, Rank>
  section(const offset_type& origin) const;

Requires: bounds().contains(origin + idx) == true for any offset_type idx such that (bounds() - origin).contains(idx) == true.

Returns: A strided view vw such that vw.data_ is (*this)[origin], vw.stride_ is stride(), and vw.bounds_ is bounds() - origin.

24.7 range access [iterator.range]

Edit within paragraph 1 as follows.

In addition to being available via inclusion of the <iterator> header, the function templates in 24.7 are available when any of the following headers are included: <array>, <coordinate>, <deque>, <forward_list>, <list>, <map>, <regex>, <set>, <string>, <unordered_map>, <unordered_set>, and <vector>.