From c4008eabd8672b07e99dc3a3c63414f8791a6dc4 Mon Sep 17 00:00:00 2001 From: luka Date: Sat, 5 Sep 2026 02:26:18 +0800 Subject: [PATCH 1/2] Implement LWG-4293 span::subspan/first/last chooses wrong constructor when T is const-qualified bool --- stl/inc/span | 14 ++++--- tests/std/tests/P0122R7_span/test.cpp | 60 +++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/stl/inc/span b/stl/inc/span index 97fe33189c4..3f5dd04b230 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -364,6 +364,8 @@ public: } // [span.sub] Subviews + // LWG-4293: The returned spans are directly-initialized, so an initializer_list + // constructor can never hijack these calls for const-qualified bool element types. template _NODISCARD constexpr auto first() const noexcept /* strengthened */ { if constexpr (_Extent != dynamic_extent) { @@ -375,7 +377,7 @@ public: } #endif // _MSVC_STL_HARDENING_SPAN || _ITERATOR_DEBUG_LEVEL != 0 - return span{_Mydata, _Count}; + return span(_Mydata, _Count); } _NODISCARD constexpr auto first(const size_type _Count) const noexcept /* strengthened */ { @@ -383,7 +385,7 @@ public: _STL_VERIFY(_Count <= _Mysize, "Count out of range in span::first(count)"); #endif - return span{_Mydata, _Count}; + return span(_Mydata, _Count); } template @@ -397,7 +399,7 @@ public: } #endif // _MSVC_STL_HARDENING_SPAN || _ITERATOR_DEBUG_LEVEL != 0 - return span{_Mydata + (_Mysize - _Count), _Count}; + return span(_Mydata + (_Mysize - _Count), _Count); } _NODISCARD constexpr auto last(const size_type _Count) const noexcept /* strengthened */ { @@ -405,7 +407,7 @@ public: _STL_VERIFY(_Count <= _Mysize, "Count out of range in span::last(count)"); #endif - return span{_Mydata + (_Mysize - _Count), _Count}; + return span(_Mydata + (_Mysize - _Count), _Count); } template @@ -427,7 +429,7 @@ public: using _ReturnType = span; - return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? _Mysize - _Offset : _Count}; + return _ReturnType(_Mydata + _Offset, _Count == dynamic_extent ? _Mysize - _Offset : _Count); } _NODISCARD constexpr auto subspan(const size_type _Offset, const size_type _Count = dynamic_extent) const noexcept @@ -439,7 +441,7 @@ public: #endif // _MSVC_STL_HARDENING_SPAN || _ITERATOR_DEBUG_LEVEL != 0 using _ReturnType = span; - return _ReturnType{_Mydata + _Offset, _Count == dynamic_extent ? _Mysize - _Offset : _Count}; + return _ReturnType(_Mydata + _Offset, _Count == dynamic_extent ? _Mysize - _Offset : _Count); } // [span.obs] Observers diff --git a/tests/std/tests/P0122R7_span/test.cpp b/tests/std/tests/P0122R7_span/test.cpp index 5b50d73a9cb..4bf597ef75f 100644 --- a/tests/std/tests/P0122R7_span/test.cpp +++ b/tests/std/tests/P0122R7_span/test.cpp @@ -996,6 +996,66 @@ constexpr bool test() { static_assert(is_same_v::reverse_iterator>); } + // LWG-4293: The subviews of a span with const-qualified bool element type must + // directly-initialize the returned span, never list-initialize it. + { + bool sequence[9]{true, false, true, false, true, false, true, false, true}; + + const span sp_dyn(sequence); + const span sp_nine(sequence); + + auto first_1 = sp_dyn.first(4); + auto first_2 = sp_nine.first<4>(); + static_assert(noexcept(sp_dyn.first(4))); // strengthened + static_assert(noexcept(sp_nine.first<4>())); // strengthened + static_assert(is_same_v>); + static_assert(is_same_v>); + assert(first_1.data() == begin(sequence)); + assert(first_2.data() == begin(sequence)); + assert(first_1.size() == 4); + assert(first_2.size() == 4); + assert(first_1[3] == false); + assert(first_2[3] == false); + + auto last_1 = sp_dyn.last(4); + auto last_2 = sp_nine.last<4>(); + static_assert(noexcept(sp_dyn.last(4))); // strengthened + static_assert(noexcept(sp_nine.last<4>())); // strengthened + static_assert(is_same_v>); + static_assert(is_same_v>); + assert(last_1.data() == begin(sequence) + 5); + assert(last_2.data() == begin(sequence) + 5); + assert(last_1.size() == 4); + assert(last_2.size() == 4); + assert(last_1[0] == false); + assert(last_2[3] == true); + + auto subspan_1 = sp_dyn.subspan(3, 4); + auto subspan_2 = sp_dyn.subspan(3); + auto subspan_3 = sp_nine.subspan<3, 4>(); + auto subspan_4 = sp_nine.subspan<3>(); + static_assert(noexcept(sp_dyn.subspan(3, 4))); // strengthened + static_assert(noexcept(sp_dyn.subspan(3))); // strengthened + static_assert(noexcept(sp_nine.subspan<3, 4>())); // strengthened + static_assert(noexcept(sp_nine.subspan<3>())); // strengthened + static_assert(is_same_v>); + static_assert(is_same_v>); + static_assert(is_same_v>); + static_assert(is_same_v>); + assert(subspan_1.data() == begin(sequence) + 3); + assert(subspan_2.data() == begin(sequence) + 3); + assert(subspan_3.data() == begin(sequence) + 3); + assert(subspan_4.data() == begin(sequence) + 3); + assert(subspan_1.size() == 4); + assert(subspan_2.size() == 6); + assert(subspan_3.size() == 4); + assert(subspan_4.size() == 6); + assert(subspan_1[0] == false); + assert(subspan_2[5] == true); + assert(subspan_3[0] == false); + assert(subspan_4[5] == true); + } + return true; } From 7804b6e6edc0e75cb30bf3472e95a68210858f1f Mon Sep 17 00:00:00 2001 From: luka Date: Sat, 5 Sep 2026 16:04:38 +0800 Subject: [PATCH 2/2] Address review feedback: remove LWG-4293 comment, use same_as decltype(auto) in tests --- stl/inc/span | 2 -- tests/std/tests/P0122R7_span/test.cpp | 27 +++++++++------------------ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/stl/inc/span b/stl/inc/span index 3f5dd04b230..ed636c6adff 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -364,8 +364,6 @@ public: } // [span.sub] Subviews - // LWG-4293: The returned spans are directly-initialized, so an initializer_list - // constructor can never hijack these calls for const-qualified bool element types. template _NODISCARD constexpr auto first() const noexcept /* strengthened */ { if constexpr (_Extent != dynamic_extent) { diff --git a/tests/std/tests/P0122R7_span/test.cpp b/tests/std/tests/P0122R7_span/test.cpp index 4bf597ef75f..d535c2584d7 100644 --- a/tests/std/tests/P0122R7_span/test.cpp +++ b/tests/std/tests/P0122R7_span/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -996,20 +997,16 @@ constexpr bool test() { static_assert(is_same_v::reverse_iterator>); } - // LWG-4293: The subviews of a span with const-qualified bool element type must - // directly-initialize the returned span, never list-initialize it. { bool sequence[9]{true, false, true, false, true, false, true, false, true}; const span sp_dyn(sequence); const span sp_nine(sequence); - auto first_1 = sp_dyn.first(4); - auto first_2 = sp_nine.first<4>(); + same_as> decltype(auto) first_1 = sp_dyn.first(4); + same_as> decltype(auto) first_2 = sp_nine.first<4>(); static_assert(noexcept(sp_dyn.first(4))); // strengthened static_assert(noexcept(sp_nine.first<4>())); // strengthened - static_assert(is_same_v>); - static_assert(is_same_v>); assert(first_1.data() == begin(sequence)); assert(first_2.data() == begin(sequence)); assert(first_1.size() == 4); @@ -1017,12 +1014,10 @@ constexpr bool test() { assert(first_1[3] == false); assert(first_2[3] == false); - auto last_1 = sp_dyn.last(4); - auto last_2 = sp_nine.last<4>(); + same_as> decltype(auto) last_1 = sp_dyn.last(4); + same_as> decltype(auto) last_2 = sp_nine.last<4>(); static_assert(noexcept(sp_dyn.last(4))); // strengthened static_assert(noexcept(sp_nine.last<4>())); // strengthened - static_assert(is_same_v>); - static_assert(is_same_v>); assert(last_1.data() == begin(sequence) + 5); assert(last_2.data() == begin(sequence) + 5); assert(last_1.size() == 4); @@ -1030,18 +1025,14 @@ constexpr bool test() { assert(last_1[0] == false); assert(last_2[3] == true); - auto subspan_1 = sp_dyn.subspan(3, 4); - auto subspan_2 = sp_dyn.subspan(3); - auto subspan_3 = sp_nine.subspan<3, 4>(); - auto subspan_4 = sp_nine.subspan<3>(); + same_as> decltype(auto) subspan_1 = sp_dyn.subspan(3, 4); + same_as> decltype(auto) subspan_2 = sp_dyn.subspan(3); + same_as> decltype(auto) subspan_3 = sp_nine.subspan<3, 4>(); + same_as> decltype(auto) subspan_4 = sp_nine.subspan<3>(); static_assert(noexcept(sp_dyn.subspan(3, 4))); // strengthened static_assert(noexcept(sp_dyn.subspan(3))); // strengthened static_assert(noexcept(sp_nine.subspan<3, 4>())); // strengthened static_assert(noexcept(sp_nine.subspan<3>())); // strengthened - static_assert(is_same_v>); - static_assert(is_same_v>); - static_assert(is_same_v>); - static_assert(is_same_v>); assert(subspan_1.data() == begin(sequence) + 3); assert(subspan_2.data() == begin(sequence) + 3); assert(subspan_3.data() == begin(sequence) + 3);