diff --git a/stl/inc/mdspan b/stl/inc/mdspan index c8a1669c2b..2fad249ec3 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -24,6 +24,10 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +// TRANSITION, non-_Ugly attribute tokens +#pragma push_macro("empty_bases") +#undef empty_bases + _STD_BEGIN template struct _Maybe_empty_array { @@ -34,7 +38,7 @@ template struct _Maybe_empty_array<_IndexType, 0> {}; template -inline constexpr size_t _Calculate_rank_dynamic = ((_Extents == dynamic_extent) + ... + 0); +inline constexpr size_t _Calculate_rank_dynamic = (static_cast(_Extents == dynamic_extent) + ... + 0); _EXPORT_STD template class extents : private _Maybe_empty_array<_IndexType, _Calculate_rank_dynamic<_Extents...>> { @@ -437,7 +441,36 @@ _EXPORT_STD struct layout_stride { }; template -class layout_left::mapping { +struct _Maybe_fully_static_extents { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + constexpr _Maybe_fully_static_extents() noexcept = default; + + template + constexpr explicit _Maybe_fully_static_extents(const _OtherExtents& _Exts_) : _Exts(_Exts_) {} + + _Extents _Exts{}; +}; + +template + requires (_Extents::rank_dynamic() == 0) +struct _Maybe_fully_static_extents<_Extents> { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + constexpr _Maybe_fully_static_extents() noexcept = default; + + template + constexpr explicit _Maybe_fully_static_extents([[maybe_unused]] const _OtherExtents& _Exts_) { +#if _CONTAINER_DEBUG_LEVEL > 0 + (void) _Extents{_Exts_}; // NB: temporary created for preconditions check +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + static constexpr _Extents _Exts{}; +}; + +template +class layout_left::mapping : private _Maybe_fully_static_extents<_Extents> { public: using extents_type = _Extents; using index_type = extents_type::index_type; @@ -445,6 +478,9 @@ public: using rank_type = extents_type::rank_type; using layout_type = layout_left; +private: + using _Base = _Maybe_fully_static_extents; + static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.left.overview]/2)."); static_assert( @@ -452,10 +488,11 @@ public: "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.left.overview]/4)."); +public: constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { + constexpr mapping(const extents_type& _Exts_) noexcept : _Base(_Exts_) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank_dynamic() != 0) { _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), @@ -469,7 +506,7 @@ public: requires is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -481,7 +518,7 @@ public: requires (extents_type::rank() <= 1) && is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_right::mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -492,7 +529,7 @@ public: template requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { index_type _Prod = 1; @@ -500,7 +537,7 @@ public: _STL_VERIFY(_Other.stride(_Idx) == _Prod, "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " "extents().fwd-prod-of-extents(r) (N4950 [mdspan.layout.left.cons]/10.1)."); - _Prod = static_cast(_Prod * _Exts.extent(_Idx)); + _Prod = static_cast(_Prod * this->_Exts.extent(_Idx)); } _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -512,11 +549,11 @@ public: constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Exts; + return this->_Exts; } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); + return _Fwd_prod_of_extents::_Calculate(this->_Exts, extents_type::_Rank); } template @@ -557,7 +594,7 @@ public: _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.left.obs]/6)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return _Fwd_prod_of_extents::_Calculate(_Exts, _Idx); + return _Fwd_prod_of_extents::_Calculate(this->_Exts, _Idx); } template @@ -567,27 +604,25 @@ public: } private: - extents_type _Exts{}; - template _NODISCARD constexpr index_type _Index_impl( [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + _STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " "[mdspan.layout.left.obs]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 index_type _Stride = 1; index_type _Result = 0; - (((_Result += _Indices * _Stride), (_Stride *= _Exts.extent(_Seq))), ...); + (((_Result += _Indices * _Stride), (_Stride *= this->_Exts.extent(_Seq))), ...); return _Result; } }; template -class layout_right::mapping { +class layout_right::mapping : private _Maybe_fully_static_extents<_Extents> { public: using extents_type = _Extents; using index_type = extents_type::index_type; @@ -595,6 +630,9 @@ public: using rank_type = extents_type::rank_type; using layout_type = layout_right; +private: + using _Base = _Maybe_fully_static_extents; + static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.right.overview]/2)."); static_assert( @@ -602,10 +640,11 @@ public: "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.right.overview]/4)."); +public: constexpr mapping() noexcept = default; constexpr mapping(const mapping&) noexcept = default; - constexpr mapping(const extents_type& _Exts_) noexcept : _Exts(_Exts_) { + constexpr mapping(const extents_type& _Exts_) noexcept : _Base(_Exts_) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank_dynamic() != 0) { _STL_VERIFY(_Exts_._Is_dynamic_multidim_index_space_size_representable(), @@ -619,7 +658,7 @@ public: requires is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -631,7 +670,7 @@ public: requires (extents_type::rank() <= 1) && is_constructible_v constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>) mapping(const layout_left::mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -643,7 +682,7 @@ public: requires is_constructible_v constexpr explicit(extents_type::rank() > 0) mapping(const layout_stride::template mapping<_OtherExtents>& _Other) noexcept - : _Exts(_Other.extents()) { + : _Base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() > 0) { index_type _Prod = 1; @@ -651,7 +690,7 @@ public: _STL_VERIFY(_Prod == _Other.stride(_Idx), "For all r in the range [0, extents_type::rank()), other.stride(r) must be equal to " "extents().rev-prod-of-extents(r) (N4950 [mdspan.layout.right.cons]/10.1)."); - _Prod = static_cast(_Prod * _Exts.extent(_Idx)); + _Prod = static_cast(_Prod * this->_Exts.extent(_Idx)); } _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -663,11 +702,11 @@ public: constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Exts; + return this->_Exts; } _NODISCARD constexpr index_type required_span_size() const noexcept { - return _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); + return _Fwd_prod_of_extents::_Calculate(this->_Exts, extents_type::_Rank); } template @@ -708,30 +747,28 @@ public: _STL_VERIFY(_Idx < extents_type::_Rank, "Value of i must be less than extents_type::rank() (N4950 [mdspan.layout.right.obs]/6)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return _Rev_prod_of_extents::_Calculate(_Exts, _Idx); + return _Rev_prod_of_extents::_Calculate(this->_Exts, _Idx); } template requires (extents_type::rank() == _OtherExtents::rank()) _NODISCARD_FRIEND constexpr bool operator==(const mapping& _Left, const mapping<_OtherExtents>& _Right) noexcept { - return _Left.extents() == _Right.extents(); + return _Left._Exts == _Right.extents(); } private: - extents_type _Exts{}; - template _NODISCARD constexpr index_type _Index_impl( [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + _STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " "[mdspan.layout.right.obs]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 index_type _Result = 0; - ((_Result = static_cast(_Indices + _Exts.extent(_Seq) * _Result)), ...); + ((_Result = static_cast(_Indices + this->_Exts.extent(_Seq) * _Result)), ...); return _Result; } }; @@ -748,7 +785,8 @@ concept _Layout_mapping_alike = requires { }; template -class layout_stride::mapping { +class layout_stride::mapping : private _Maybe_fully_static_extents<_Extents>, + private _Maybe_empty_array { public: using extents_type = _Extents; using index_type = extents_type::index_type; @@ -756,6 +794,10 @@ public: using rank_type = extents_type::rank_type; using layout_type = layout_stride; +private: + using _Extents_base = _Maybe_fully_static_extents; + using _Strides_base = _Maybe_empty_array; + static_assert(_Is_extents, "Extents must be a specialization of std::extents (N4950 [mdspan.layout.stride.overview]/2)."); static_assert( @@ -763,12 +805,14 @@ public: "If Extents::rank_dynamic() == 0 is true, then the size of the multidimensional index space Extents() must be " "representable as a value of type typename Extents::index_type (N4950 [mdspan.layout.stride.overview]/4)."); - constexpr mapping() noexcept : _Exts(extents_type{}) { +public: + constexpr mapping() noexcept : _Extents_base(extents_type{}) { if constexpr (extents_type::rank() != 0) { - _Strides.back() = 1; + this->_Array.back() = 1; for (rank_type _Idx = extents_type::_Rank - 1; _Idx-- > 0;) { #if _CONTAINER_DEBUG_LEVEL > 0 - const bool _Overflow = _Mul_overflow(_Strides[_Idx + 1], _Exts.extent(_Idx + 1), _Strides[_Idx]); + const bool _Overflow = + _Mul_overflow(this->_Array[_Idx + 1], this->_Exts.extent(_Idx + 1), this->_Array[_Idx]); // NB: N4950 requires value of 'layout_right::mapping().required_span_size()' to be // representable as value of type 'index_type', but this is not enough. We need to require every single // stride to be representable as value of type 'index_type', so we can get desired effects. @@ -776,7 +820,7 @@ public: "Value of layout_right::mapping().required_span_size() must be " "representable as a value of type index_type (N4950 [mdspan.layout.stride.cons]/1)."); #else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv - _Strides[_Idx] = static_cast(_Strides[_Idx + 1] * _Exts.extent(_Idx + 1)); + this->_Array[_Idx] = static_cast(this->_Array[_Idx + 1] * this->_Exts.extent(_Idx + 1)); #endif // _CONTAINER_DEBUG_LEVEL > 0 } } @@ -789,17 +833,17 @@ public: && is_nothrow_constructible_v constexpr mapping(const extents_type& _Exts_, span<_OtherIndexType, extents_type::rank()> _Strides_, index_sequence<_Indices...>) noexcept - : _Exts(_Exts_), _Strides{static_cast(_STD as_const(_Strides_[_Indices]))...} { + : _Extents_base(_Exts_), _Strides_base{static_cast(_STD as_const(_Strides_[_Indices]))...} { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (extents_type::rank() != 0) { bool _Found_zero = false; bool _Overflow = false; index_type _Req_span_size = 0; for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - const index_type _Stride = _Strides[_Idx]; + const index_type _Stride = this->_Array[_Idx]; _STL_VERIFY(_Stride > 0, "Value of s[i] must be greater than 0 for all i in the range [0, rank_) " "(N4950 [mdspan.layout.stride.cons]/4.1)."); - const index_type _Ext = _Exts.extent(_Idx); + const index_type _Ext = this->_Exts.extent(_Idx); if (_Ext == 0) { _Found_zero = true; } @@ -852,7 +896,7 @@ public: && (_Is_mapping_of || _Is_mapping_of || _Is_mapping_of) )) mapping(const _StridedLayoutMapping& _Other) noexcept - : _Exts(_Other.extents()) { + : _Extents_base(_Other.extents()) { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_STD in_range(_Other.required_span_size()), "Value of other.required_span_size() must be representable as a value of type index_type (N4950 " @@ -866,18 +910,18 @@ public: _STL_VERIFY(_Stride > 0, "Value of other.stride(r) must be greater than 0 for every rank index r of " "extents() (N4950 [mdspan.layout.stride.cons]/7.2)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 - _Strides[_Idx] = static_cast(_Stride); + this->_Array[_Idx] = static_cast(_Stride); } } constexpr mapping& operator=(const mapping&) noexcept = default; _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Exts; + return this->_Exts; } _NODISCARD constexpr array strides() const noexcept { - return _Strides; + return this->_Array; } _NODISCARD constexpr index_type required_span_size() const noexcept { @@ -886,12 +930,12 @@ public: } else { index_type _Result = 1; for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { - const index_type _Ext = _Exts.extent(_Idx); + const index_type _Ext = this->_Exts.extent(_Idx); if (_Ext == 0) { return 0; } - _Result += (_Ext - 1) * _Strides[_Idx]; + _Result += (_Ext - 1) * this->_Array[_Idx]; } return _Result; @@ -925,7 +969,8 @@ public: if constexpr (extents_type::rank() == 0) { return true; } else { - return required_span_size() == _Fwd_prod_of_extents::_Calculate(_Exts, extents_type::_Rank); + return required_span_size() + == _Fwd_prod_of_extents::_Calculate(this->_Exts, extents_type::_Rank); } } @@ -934,7 +979,7 @@ public: } _NODISCARD constexpr index_type stride(const rank_type _Idx) const noexcept { - return _Strides[_Idx]; + return this->_Array[_Idx]; } template @@ -957,9 +1002,6 @@ public: } private: - extents_type _Exts{}; - array _Strides{}; - template _NODISCARD static constexpr _OtherMapping::index_type _Offset(_OtherMapping& _Mapping) noexcept { if constexpr (extents_type::rank() == 0) { @@ -981,12 +1023,12 @@ private: [[maybe_unused]] index_sequence<_Seq...> _Index_seq, _IndexTypes... _Indices) const noexcept { _STL_INTERNAL_STATIC_ASSERT((same_as<_IndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), + _STL_VERIFY(this->_Exts._Contains_multidimensional_index(_Index_seq, _Indices...), "Value of extents_type::index-cast(i) must be a multidimensional index in extents_ (N4950 " "[mdspan.layout.stride.obs]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return static_cast(((_Indices * _Strides[_Seq]) + ... + 0)); + return static_cast(((_Indices * this->_Array[_Seq]) + ... + 0)); } }; @@ -1019,9 +1061,81 @@ struct default_accessor { } }; +template +concept _Elidable_layout_mapping = + (_Is_any_of_v<_LayoutPolicy, layout_left, layout_right> && _Extents::rank_dynamic() == 0) + || (same_as<_LayoutPolicy, layout_stride> && _Extents::rank() == 0); + +template +struct _Mdspan_mapping_base { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + using _Mapping = _LayoutPolicy::template mapping<_Extents>; + + constexpr _Mdspan_mapping_base() noexcept = default; + + constexpr explicit _Mdspan_mapping_base(const _Extents& _Exts) : _Map(_Exts) {} + + template + constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) : _Map(_Map_) {} + + _Mapping _Map{}; +}; + +template _LayoutPolicy> +struct _Mdspan_mapping_base<_Extents, _LayoutPolicy> { + _STL_INTERNAL_STATIC_ASSERT(_Is_extents<_Extents>); + + using _Mapping = _LayoutPolicy::template mapping<_Extents>; + + constexpr _Mdspan_mapping_base() noexcept = default; + + constexpr explicit _Mdspan_mapping_base(const _Extents&) noexcept {} + + template + constexpr explicit _Mdspan_mapping_base(const _OtherMapping& _Map_) { + // NB: Constructing _Mapping from _OtherMapping may have side effects - we should create a temporary. + if constexpr (!_Elidable_layout_mapping) { + (void) _Mapping{_Map_}; + } + } + + static constexpr _Mapping _Map{}; +}; + +template +concept _Elidable_accessor_policy = _Is_specialization_v<_AccessorPolicy, default_accessor>; + +template +struct _Mdspan_accessor_base { + constexpr _Mdspan_accessor_base() noexcept = default; + + template + constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) : _Acc(_Acc_) {} + + _AccessorPolicy _Acc{}; +}; + +template <_Elidable_accessor_policy _AccessorPolicy> +struct _Mdspan_accessor_base<_AccessorPolicy> { + constexpr _Mdspan_accessor_base() noexcept = default; + + template + constexpr explicit _Mdspan_accessor_base(const _OtherAccessorPolicy& _Acc_) { + // NB: Constructing _AccessorPolicy from _OtherAccessorPolicy may have side effects - we should create a + // temporary. + if constexpr (!_Elidable_accessor_policy<_OtherAccessorPolicy>) { + (void) _AccessorPolicy{_Acc_}; + } + } + + static constexpr _AccessorPolicy _Acc{}; +}; + _EXPORT_STD template > -class mdspan { +class __declspec(empty_bases) mdspan : private _Mdspan_mapping_base<_Extents, _LayoutPolicy>, + private _Mdspan_accessor_base<_AccessorPolicy> { public: using extents_type = _Extents; using layout_type = _LayoutPolicy; @@ -1035,6 +1149,10 @@ public: using data_handle_type = accessor_type::data_handle_type; using reference = accessor_type::reference; +private: + using _Mapping_base = _Mdspan_mapping_base; + using _Accessor_base = _Mdspan_accessor_base; + static_assert( sizeof(element_type) > 0, "ElementType must be a complete type (N4950 [mdspan.mdspan.overview]/2.1)."); static_assert( @@ -1047,6 +1165,7 @@ public: "ElementType and typename AccessorPolicy::element_type must be the same type (N4950 " "[mdspan.mdspan.overview]/2.3)."); +public: _NODISCARD static constexpr rank_type rank() noexcept { return extents_type::_Rank; } @@ -1063,7 +1182,7 @@ public: } _NODISCARD constexpr index_type extent(const rank_type _Idx) const noexcept { - return _Map.extents().extent(_Idx); + return this->_Map.extents().extent(_Idx); } constexpr mdspan() @@ -1080,7 +1199,8 @@ public: && (sizeof...(_OtherIndexTypes) == rank() || sizeof...(_OtherIndexTypes) == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit mdspan(data_handle_type _Ptr_, _OtherIndexTypes... _Exts) - : _Ptr(_STD move(_Ptr_)), _Map(extents_type{static_cast(_STD move(_Exts))...}), _Acc() {} + : _Mapping_base(extents_type{static_cast(_STD move(_Exts))...}), _Accessor_base(), + _Ptr(_STD move(_Ptr_)) {} template requires is_convertible_v<_OtherIndexType, index_type> @@ -1088,7 +1208,7 @@ public: && (_Size == rank() || _Size == rank_dynamic()) && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, span<_OtherIndexType, _Size> _Exts) - : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} + : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} template requires is_convertible_v @@ -1097,18 +1217,18 @@ public: && is_constructible_v && is_default_constructible_v constexpr explicit(_Size != rank_dynamic()) mdspan(data_handle_type _Ptr_, const array<_OtherIndexType, _Size>& _Exts) - : _Ptr(_STD move(_Ptr_)), _Map(extents_type{_Exts}), _Acc() {} + : _Mapping_base(extents_type{_Exts}), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} - constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Ext) + constexpr mdspan(data_handle_type _Ptr_, const extents_type& _Exts) requires is_constructible_v && is_default_constructible_v - : _Ptr(_STD move(_Ptr_)), _Map(_Ext), _Acc() {} + : _Mapping_base(_Exts), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_) requires is_default_constructible_v - : _Ptr(_STD move(_Ptr_)), _Map(_Map_), _Acc() {} + : _Mapping_base(_Map_), _Accessor_base(), _Ptr(_STD move(_Ptr_)) {} constexpr mdspan(data_handle_type _Ptr_, const mapping_type& _Map_, const accessor_type& _Acc_) - : _Ptr(_STD move(_Ptr_)), _Map(_Map_), _Acc(_Acc_) {} + : _Mapping_base(_Map_), _Accessor_base(_Acc_), _Ptr(_STD move(_Ptr_)) {} template requires is_constructible_v&> @@ -1117,7 +1237,7 @@ public: !is_convertible_v&, mapping_type> || !is_convertible_v) mdspan(const mdspan<_OtherElementType, _OtherExtents, _OtherLayoutPolicy, _OtherAccessor>& _Other) - : _Ptr(_Other.data_handle()), _Map(_Other.mapping()), _Acc(_Other.accessor()) { + : _Mapping_base(_Other.mapping()), _Accessor_base(_Other.accessor()), _Ptr(_Other.data_handle()) { static_assert(is_constructible_v, "The data_handle_type must be constructible from const typename OtherAccessor::data_handle_type& (N4950 " "[mdspan.mdspan.cons]/20.1)."); @@ -1175,20 +1295,20 @@ public: _NODISCARD constexpr size_type size() const noexcept { #if _CONTAINER_DEBUG_LEVEL > 0 if constexpr (rank_dynamic() != 0) { - _STL_VERIFY(_Map.extents().template _Is_dynamic_multidim_index_space_size_representable(), + _STL_VERIFY(this->_Map.extents().template _Is_dynamic_multidim_index_space_size_representable(), "The size of the multidimensional index space extents() must be representable as a value of type " "size_type (N4950 [mdspan.mdspan.members]/7)."); } #endif // _CONTAINER_DEBUG_LEVEL > 0 return static_cast( - _Fwd_prod_of_extents::_Calculate(_Map.extents(), extents_type::_Rank)); + _Fwd_prod_of_extents::_Calculate(this->_Map.extents(), extents_type::_Rank)); } _NODISCARD constexpr bool empty() const noexcept { if constexpr (extents_type::_Multidim_index_space_size_is_always_zero) { return true; } else { - const extents_type& _Exts = _Map.extents(); + const extents_type& _Exts = this->_Map.extents(); for (rank_type _Idx = 0; _Idx < extents_type::_Rank; ++_Idx) { if (_Exts.extent(_Idx) == 0) { return true; @@ -1200,12 +1320,18 @@ public: friend constexpr void swap(mdspan& _Left, mdspan& _Right) noexcept { swap(_Left._Ptr, _Right._Ptr); // intentional ADL - swap(_Left._Map, _Right._Map); // intentional ADL - swap(_Left._Acc, _Right._Acc); // intentional ADL + + if constexpr (!_Elidable_layout_mapping) { + swap(_Left._Map, _Right._Map); // intentional ADL + } + + if constexpr (!_Elidable_accessor_policy) { + swap(_Left._Acc, _Right._Acc); // intentional ADL + } } _NODISCARD constexpr const extents_type& extents() const noexcept { - return _Map.extents(); + return this->_Map.extents(); } _NODISCARD constexpr const data_handle_type& data_handle() const noexcept { @@ -1213,11 +1339,11 @@ public: } _NODISCARD constexpr const mapping_type& mapping() const noexcept { - return _Map; + return this->_Map; } _NODISCARD constexpr const accessor_type& accessor() const noexcept { - return _Acc; + return this->_Acc; } _NODISCARD static constexpr bool is_always_unique() noexcept( @@ -1235,21 +1361,21 @@ public: return mapping_type::is_always_strided(); } - _NODISCARD constexpr bool is_unique() const noexcept(noexcept(_Map.is_unique())) /* strengthened */ { - return _Map.is_unique(); + _NODISCARD constexpr bool is_unique() const noexcept(noexcept(this->_Map.is_unique())) /* strengthened */ { + return this->_Map.is_unique(); } - _NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(_Map.is_exhaustive())) /* strengthened */ { - return _Map.is_exhaustive(); + _NODISCARD constexpr bool is_exhaustive() const noexcept(noexcept(this->_Map.is_exhaustive())) /* strengthened */ { + return this->_Map.is_exhaustive(); } - _NODISCARD constexpr bool is_strided() const noexcept(noexcept(_Map.is_strided())) /* strengthened */ { - return _Map.is_strided(); + _NODISCARD constexpr bool is_strided() const noexcept(noexcept(this->_Map.is_strided())) /* strengthened */ { + return this->_Map.is_strided(); } _NODISCARD constexpr index_type stride(const rank_type _Idx) const - noexcept(noexcept(_Map.stride(_Idx))) /* strengthened */ { - return _Map.stride(_Idx); + noexcept(noexcept(this->_Map.stride(_Idx))) /* strengthened */ { + return this->_Map.stride(_Idx); } private: @@ -1264,16 +1390,14 @@ private: _NODISCARD constexpr reference _Access_impl(_OtherIndexTypes... _Indices) const { _STL_INTERNAL_STATIC_ASSERT((same_as<_OtherIndexTypes, index_type> && ...)); #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Map.extents()._Contains_multidimensional_index(make_index_sequence{}, _Indices...), + _STL_VERIFY(this->_Map.extents()._Contains_multidimensional_index(make_index_sequence{}, _Indices...), "I must be a multidimensional index in extents() (N4950 [mdspan.mdspan.members]/3)."); #endif // _CONTAINER_DEBUG_LEVEL > 0 - return _Acc.access(_Ptr, static_cast(_Map(_Indices...))); + return this->_Acc.access(_Ptr, static_cast(this->_Map(_Indices...))); } - data_handle_type _Ptr{}; - mapping_type _Map{}; - accessor_type _Acc{}; + /* [[no_unique_address]] */ data_handle_type _Ptr{}; }; template @@ -1309,6 +1433,9 @@ mdspan(const typename _AccessorType::data_handle_type&, const _MappingType&, con _STD_END +// TRANSITION, non-_Ugly attribute tokens +#pragma pop_macro("empty_bases") + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp index d5534e2234..7bed2d659c 100644 --- a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp +++ b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp @@ -13,6 +13,7 @@ #define intrinsic 3 #define lifetimebound 4 #define noop_dtor 5 +#define empty_bases 6 #include <__msvc_all_public_headers.hpp> @@ -35,3 +36,7 @@ #if noop_dtor != 5 #error bad macro expansion #endif // noop_dtor != 5 + +#if empty_bases != 6 +#error bad macro expansion +#endif // noop_dtor != 6 diff --git a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp index 9036a910c7..5402186a9f 100644 --- a/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_default_accessor/test.cpp @@ -27,6 +27,9 @@ constexpr void test_one(array elems) { static_assert(is_trivially_copyable_v); static_assert(semiregular); + // Check if default_accessor is empty + static_assert(std::is_empty_v); + // Check nested types static_assert(same_as); static_assert(same_as); diff --git a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp index be1ae79656..4c53b23742 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_left/test.cpp @@ -423,6 +423,10 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } + +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // 3x2 matrix with column-major order const array values{0, 1, 2, 3, 4, 5}; mdspan, layout_left> matrix{values.data()}; @@ -444,6 +448,9 @@ constexpr void check_correctness() { #endif // ^^^ !defined(__cpp_multidimensional_subscript) ^^^ } +#ifdef __clang__ + if (!is_constant_evaluated()) // FIXME clang hits constexpr limit here +#endif { // 3x2x4 tensor const array values{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; mdspan, layout_left> tensor{values.data(), 3, 2, 4}; @@ -493,6 +500,12 @@ constexpr void check_correctness() { } } +// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(is_empty_v>>); +static_assert(is_empty_v>>); + constexpr bool test() { check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time diff --git a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp index f7d9f80137..273a461ecf 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_right/test.cpp @@ -516,6 +516,12 @@ constexpr void check_correctness() { } } +// When 'M::extents_type::rank_dynamic()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(is_empty_v>>); +static_assert(is_empty_v>>); + constexpr bool test() { check_members_with_various_extents([](const E& e) { check_members(e, make_index_sequence{}); }); if (!is_constant_evaluated()) { // too heavy for compile time diff --git a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp index ab20ad5c87..13f6ea3032 100644 --- a/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_layout_stride/test.cpp @@ -821,6 +821,12 @@ constexpr void check_correctness() { } } +// When 'M::extents_type::rank()' is equal to 0 then 'is_empty_v' should be true (MSVC STL specific behavior) +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(!is_empty_v>>); +static_assert(is_empty_v>>); + constexpr bool test() { // Check signed integers check_members(extents{5}, array{1}); diff --git a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp index 44580c94dd..aa4915c84c 100644 --- a/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp +++ b/tests/std/tests/P0009R18_mdspan_mdspan/test.cpp @@ -1095,35 +1095,52 @@ constexpr void check_empty() { } constexpr void check_swap() { - using E = extents; - using Mds = mdspan, TrackingAccessor>; - static_assert(is_nothrow_swappable_v); - static_assert(!is_swappable_v); - - int a1[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - Mds mds1{TrackingDataHandle{1, a1}, TrackingLayout<>::mapping(1), TrackingAccessor{1}}; - int a2[9] = {3, 0, 0, 0, 3, 0, 0, 0, 3}; - Mds mds2{TrackingDataHandle{3, a2}, TrackingLayout<>::mapping(3), TrackingAccessor{3}}; - swap(mds1, mds2); - static_assert(is_void_v); - - assert(mds1.data_handle().get_id() == 3); - assert(mds1.data_handle().is_swapped()); - assert(mds1.mapping().get_id() == 3); - assert(mds1.mapping().is_swapped()); - assert(mds1.accessor().get_id() == 3); - assert(mds1.accessor().is_swapped()); - assert((mds1[array{1, 1}] == 3)); - assert((mds1[array{0, 1}] == 0)); - - assert(mds2.data_handle().get_id() == 1); - assert(mds2.data_handle().is_swapped()); - assert(mds2.mapping().get_id() == 1); - assert(mds2.mapping().is_swapped()); - assert(mds2.accessor().get_id() == 1); - assert(mds2.accessor().is_swapped()); - assert((mds2[array{1, 1}] == 1)); - assert((mds2[array{0, 1}] == 0)); + { // Check swapping with tracking types + using E = extents; + using Mds = mdspan, TrackingAccessor>; + static_assert(is_nothrow_swappable_v); + static_assert(!is_swappable_v); + + int a1[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + Mds mds1{TrackingDataHandle{1, a1}, TrackingLayout<>::mapping(1), TrackingAccessor{1}}; + int a2[9] = {3, 0, 0, 0, 3, 0, 0, 0, 3}; + Mds mds2{TrackingDataHandle{3, a2}, TrackingLayout<>::mapping(3), TrackingAccessor{3}}; + swap(mds1, mds2); + static_assert(is_void_v); + + assert(mds1.data_handle().get_id() == 3); + assert(mds1.data_handle().is_swapped()); + assert(mds1.mapping().get_id() == 3); + assert(mds1.mapping().is_swapped()); + assert(mds1.accessor().get_id() == 3); + assert(mds1.accessor().is_swapped()); + assert((mds1[array{1, 1}] == 3)); + assert((mds1[array{0, 1}] == 0)); + + assert(mds2.data_handle().get_id() == 1); + assert(mds2.data_handle().is_swapped()); + assert(mds2.mapping().get_id() == 1); + assert(mds2.mapping().is_swapped()); + assert(mds2.accessor().get_id() == 1); + assert(mds2.accessor().is_swapped()); + assert((mds2[array{1, 1}] == 1)); + assert((mds2[array{0, 1}] == 0)); + } + + { // Check swapping with standard layout and accessor + using Mds = mdspan>; + static_assert(is_nothrow_swappable_v); + static_assert(!is_swappable_v); + + int diag[] = {1, 0, 0, 1}; + Mds mds1{diag}; + int revdiag[] = {0, 1, 1, 0}; + Mds mds2{revdiag}; + + swap(mds1, mds2); + assert(mds1.data_handle() == revdiag); + assert(mds2.data_handle() == diag); + } } constexpr void check_getters() { @@ -1316,11 +1333,33 @@ constexpr void check_deduction_guides() { } } +// When +// * 'Mds::accessor_type' is specialization of 'default_accesor', and +// * 'Mds::layout_type' is +// * 'layout_left' or 'layout_right' and 'Mds::extents_type::rank_dynamic() == 0', or +// * 'layout_stride' and 'Mds::extents_type::rank() == 0' +// then 'sizeof(Mds) == sizeof(void*)' (MSVC STL specific behavior). +static_assert(sizeof(mdspan, layout_left>) == sizeof(void*)); +static_assert(sizeof(mdspan, layout_left>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_left, TrivialAccessor>) > sizeof(void*)); + +static_assert(sizeof(mdspan, layout_right>) == sizeof(void*)); +static_assert(sizeof(mdspan, layout_right>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_right, TrivialAccessor>) > sizeof(void*)); + +static_assert(sizeof(mdspan, layout_stride>) == sizeof(void*)); +static_assert(sizeof(mdspan, layout_stride>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_stride>) > sizeof(void*)); +static_assert(sizeof(mdspan, layout_stride, TrivialAccessor>) > sizeof(void*)); + constexpr bool test() { + check_modeled_concepts_and_member_types, layout_left, default_accessor>(); + check_modeled_concepts_and_member_types, layout_right, default_accessor>(); + check_modeled_concepts_and_member_types, layout_stride, default_accessor>(); + check_modeled_concepts_and_member_types, layout_left, TrackingAccessor>(); check_modeled_concepts_and_member_types, layout_stride, TrivialAccessor>(); check_modeled_concepts_and_member_types, TrackingLayout<>, AccessorWithTrackingDataHandle>(); - check_modeled_concepts_and_member_types, layout_left, TrackingAccessor>(); check_observers(); check_default_constructor(); check_defaulted_copy_and_move_constructors();