This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
Section: 23.7.2.2.4 [span.sub] Status: New Submitter: Yuhan Liu Opened: 2025-07-11 Last modified: 2025-07-11
Priority: Not Prioritized
View all other issues in [span.sub].
View all issues with New 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.
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 <= Extent
istrue
.-2- Hardened preconditions:
Count <= size()
istrue
.-3- Effects: Equivalent to:
return R(
where{data(), Count});R
is the return type.template<size_t Count> constexpr span<element_type, Count> last() const;
-4- Mandates:
Count <= Extent
istrue
.-5- Hardened preconditions:
Count <= size()
istrue
.-6- Effects: Equivalent to:
return R(
where{data() + (size() - Count), Count});R
is 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
span
type 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});R
is 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});R
is 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() - offset
true
.-16- Effects: Equivalent to:
wherereturn R(
{data() + offset, count == dynamic_extent ? size() - offset : count});R
is the return type.