This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Tentatively Ready status.
Section: 23.7.2.2.4 [span.sub] Status: Tentatively Ready Submitter: Yuhan Liu Opened: 2025-07-11 Last modified: 2025-08-26
Priority: Not Prioritized
View all other issues in [span.sub].
View all issues with Tentatively Ready status.
Discussion:
In section 23.7.2.2.4 [span.sub], paragraphs p12, p14, and p16 erroneously use the initializer list constructor for span instead of the intended iterator/count constructor.
Specifically, in these paragraphs, the standard states:
Effects: Equivalent to: return {data(), count};
or some variant of return {pointer, size}. As reported in
GCC bug 120997
this results in a span that points to invalid stack memory.
This can be reproduced on GCC 15.1 for subspan, first, and last:
https://godbolt.org/z/r9nrdWscq.
A proposed fix (thanks to Jonathan Wakely) could look like this following:
return span<element_type>(data(), count);
for the affected paragraphs,
which would explicitly specify the constructor used.
[2025-07-11; Jonathan adds proposed resolution]
The meaning of those Effects: paragraphs was changed for C++26 by
P2447R6 which added the span(initializer_list) constructor.
A simpler demo is:
The proposed resolution is to usebool a[5]{}; std::span<const bool> s(a); std::span<const bool> s2 = s.first(5); assert(s2.size() == 5); // OK in C++23, fails in C++26 assert(s2.data() == a); // OK in C++23, fails in C++26
R(data(), count) instead of
{data(), count}. The former always (uniformly) means the same thing,
but for the latter the meaning of list-initialization depends on the types.
The list-initialization form will choose the initializer-list constructor
when data() and count are both convertible to the element type.
[2025-08-21; Reflector poll]
Set status to Tentatively Ready after nine votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 23.7.2.2.4 [span.sub] as indicated:
template<size_t Count> constexpr span<element_type, Count> first() const;-1- Mandates:
Count <= Extentistrue.-2- Hardened preconditions:
Count <= size()istrue.-3- Effects: Equivalent to:
return R(where{data(), Count});Ris the return type.template<size_t Count> constexpr span<element_type, Count> last() const;-4- Mandates:
Count <= Extentistrue.-5- Hardened preconditions:
Count <= size()istrue.-6- Effects: Equivalent to:
return R(where{data() + (size() - Count), Count});Ris the return type.template<size_t Offset, size_t Count = dynamic_extent> constexpr span<element_type, see below> subspan() const;-7- Mandates:
isOffset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset)true.-8- Hardened preconditions:
isOffset <= size() && (Count == dynamic_extent || Count <= size() - Offset)true.-9- Effects: Equivalent to:
return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);-10- Remarks: The second template argument of the returned
spantype is:Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)constexpr span<element_type, dynamic_extent> first(size_type count) const;-11- Hardened preconditions:
count <= size()istrue.-12- Effects: Equivalent to:
return R(where{data(), count});Ris the return type.constexpr span<element_type, dynamic_extent> last(size_type count) const;-13- Hardened preconditions:
count <= size()istrue.-14- Effects: Equivalent to:
return R(where{data() + (size() - count), count});Ris the return type.constexpr span<element_type, dynamic_extent> subspan( size_type offset, size_type count = dynamic_extent) const;-15- Hardened preconditions:
isoffset <= size() && (count == dynamic_extent || count <= size() - offsettrue.-16- Effects: Equivalent to:
wherereturn R({data() + offset, count == dynamic_extent ? size() - offset : count});Ris the return type.