Title
span::subspan/first/last chooses wrong constructor when T is const-qualified bool
Status
new
Section
[span.sub]
Submitter
Yuhan Liu

Created on 2025-07-11.00:00:00 last changed 7 days ago

Messages

Date: 2025-07-11.09:51:34

Proposed resolution:

This wording is relative to N5008.

  1. Modify [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.

Date: 2025-07-15.00:00:00

[ 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.

Date: 2025-07-11.00:00:00

In section [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.

History
Date User Action Args
2025-07-11 09:51:34adminsetmessages: + msg14893
2025-07-11 09:51:34adminsetmessages: + msg14892
2025-07-11 00:00:00admincreate