``` #include <span> #include <iostream> #include <array> auto main() -> int { std::array<bool, 5> data{}; std::span<const bool> data_view(data.data(), 5); std::cout << "data_view.data():\t" << data_view.data() << '\n'; std::cout << "data_view.size():\t" << data_view.size() << '\n'; std::cout << "========================================\n"; std::span<const bool> data_view_2 = data_view.subspan(0, 5); std::cout << "data_view_2.data():\t" << data_view_2.data() << '\n'; std::cout << "data_view_2.size():\t" << data_view_2.size() << '\n'; std::cout << "========================================\n"; return 0; } ``` (Godbolt: https://godbolt.org/z/1Ta4rrsb5 ) The above code when compiled with "-std=c++26 -Wall -Wextra" prints ``` data_view.data(): 0x7ffc7ca19b9b data_view.size(): 5 ======================================== data_view_2.data(): 0x7ffc7ca19b3e data_view_2.size(): 2 ======================================== ``` Namely, `data_view_2` is a size 2 span even though it was created with `data_view.subspan(0, 5)`. It seems like the return object in `subspan` (C++26) is being falsely interpreted as an initializer list rather than a pointer and size, causing `data_view_2` to point to the initializer list on stack (which is immediately invalidated upon return). This bug does not occur on C++23 (-std=c++23). It also seems to only occur for `const bool` (not `bool`, not `int`, etc.).
This fixes it: --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -454,7 +454,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __glibcxx_assert(__count <= size()); __glibcxx_assert(__offset + __count <= size()); } - return {this->data() + __offset, __count}; + return span<element_type>(this->data() + __offset, __count); } private: But we need the same fix in span::first and span::last, and maybe elsewhere. That's needed because of this new constructor in C++26 mode: #if __cpp_lib_span_initializer_list >= 202311L // >= C++26 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winit-list-lifetime" constexpr explicit(extent != dynamic_extent) span(initializer_list<value_type> __il) requires (is_const_v<_Type>) : _M_ptr(__il.begin()), _M_extent(__il.size()) { } #pragma GCC diagnostic pop #endif I haven't figured out yet whether G++ is correct to try to use that constructor here.
The libstdc++ implementation follows the C++26 draft standard exactly: Effects: Equivalent to: return {data(), count}; So maybe we need to fix that.
Yeah we have a defect in the C++26 draft, which needs to be fixed.
Thanks for submitting the issue! I'll add the link here once it gets created.
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>: https://gcc.gnu.org/g:a72d0e1a8bf0770ddf1d8d0ebe589f92a4fab4ef commit r16-2152-ga72d0e1a8bf0770ddf1d8d0ebe589f92a4fab4ef Author: Jonathan Wakely <jwakely@redhat.com> Date: Tue Jul 8 14:56:39 2025 +0100 libstdc++: Do not use list-initialization in std::span members [PR120997] As the bug report shows, for span<const bool> the return statements of the form `return {data(), count};` will use the new C++26 constructor, span(initializer_list<element_type>). Although the conversions from data() to bool and count to bool are narrowing and should be ill-formed, in system headers the narrowing diagnostics are suppressed. In any case, even if the compiler diagnosed them as ill-formed, we still don't want the initializer_list constructor to be used. We want to use the span(element_type*, size_t) constructor instead. Replace the braced-init-list uses with S(data(), count) where S is the correct return type. We need to make similar changes in the C++26 working draft, which will be taken care of via an LWG issue. libstdc++-v3/ChangeLog: PR libstdc++/120997 * include/std/span (span::first, span::last, span::subspan): Do not use braced-init-list for return statements. * testsuite/23_containers/span/120997.cc: New test.
(In reply to Jonathan Wakely from comment #4) > Thanks for submitting the issue! I'll add the link here once it gets created. https://cplusplus.github.io/LWG/issue4293
The releases/gcc-15 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>: https://gcc.gnu.org/g:ab3781665da064985d66de0c895cc43588179cb6 commit r15-9952-gab3781665da064985d66de0c895cc43588179cb6 Author: Jonathan Wakely <jwakely@redhat.com> Date: Tue Jul 8 14:56:39 2025 +0100 libstdc++: Do not use list-initialization in std::span members [PR120997] As the bug report shows, for span<const bool> the return statements of the form `return {data(), count};` will use the new C++26 constructor, span(initializer_list<element_type>). Although the conversions from data() to bool and count to bool are narrowing and should be ill-formed, in system headers the narrowing diagnostics are suppressed. In any case, even if the compiler diagnosed them as ill-formed, we still don't want the initializer_list constructor to be used. We want to use the span(element_type*, size_t) constructor instead. Replace the braced-init-list uses with S(data(), count) where S is the correct return type. We need to make similar changes in the C++26 working draft, which will be taken care of via an LWG issue. libstdc++-v3/ChangeLog: PR libstdc++/120997 * include/std/span (span::first, span::last, span::subspan): Do not use braced-init-list for return statements. * testsuite/23_containers/span/120997.cc: New test. (cherry picked from commit a72d0e1a8bf0770ddf1d8d0ebe589f92a4fab4ef)
Fixed for GCC 15.2