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.

4293. span::subspan/first/last chooses wrong constructor when T is const-qualified bool

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:

bool 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
The proposed resolution is to use 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.

  1. 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 is true.

    -2- Hardened preconditions: Count <= size() is true.

    -3- Effects: Equivalent to: return R({data(), Count}); where R is the return type.

    
    template<size_t Count> constexpr span<element_type, Count> last() const;
    

    -4- Mandates: Count <= Extent is true.

    -5- Hardened preconditions: Count <= size() is true.

    -6- Effects: Equivalent to: return R({data() + (size() - Count), Count}); where R is the return type.

    
    template<size_t Offset, size_t Count = dynamic_extent>
      constexpr span<element_type, see below> subspan() const;
    

    -7- Mandates:

    Offset <= Extent && (Count == dynamic_extent || Count <= Extent - Offset)
    
    is true.

    -8- Hardened preconditions:

    Offset <= size() && (Count == dynamic_extent || Count <= size() - Offset)
    
    is 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() is true.

    -12- Effects: Equivalent to: return R({data(), count}); where R is the return type.

    
    constexpr span<element_type, dynamic_extent> last(size_type count) const;
    

    -13- Hardened preconditions: count <= size() is true.

    -14- Effects: Equivalent to: return R({data() + (size() - count), count}); where R is the return type.

    
    constexpr span<element_type, dynamic_extent> subspan(
      size_type offset, size_type count = dynamic_extent) const;
    

    -15- Hardened preconditions:

    offset <= size() && (count == dynamic_extent || count <= size() - offset
    
    is true.

    -16- Effects: Equivalent to:

    return R({data() + offset, count == dynamic_extent ? size() - offset :  count});
    
    where R is the return type.