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.

3813. std::span<volatile T, E> is made ill-formed by P2278R4 when T is a normal class type

Section: 23.7.2.2.1 [span.overview] Status: New Submitter: Jiang An Opened: 2022-11-06 Last modified: 2025-03-22

Priority: 2

View all other issues in [span.overview].

View all issues with New status.

Discussion:

This issue is discovered when implementing the span part of P2278R4 in MSVC STL.

P2278R4 added the const_iterator member type to std::span, which required its iterator type to model std::input_iterator (same for const_reverse_iterator and reverse_iterator).

However, when element_type is volatile T and T is a class type, the iterator type generally fails to satisfy input_iterator, because:

  1. input_iterator<iterator> requires indirectly_readable<iterator>;

  2. indirectly_readable<iterator> requires common_reference_with<iterator_reference_t<iterator>&&, iterator_rvalue_reference_t<iterator>&&>, that is common_reference_with<volatile T&, volatile T&&>;

  3. common_reference_t<volatile T&, volatile T&&> is T (which is problematic), and thus common_reference_with<volatile T&, volatile T&&> requires both convertible_to<volatile T&, T> and convertible_to<volatile T&&, T>;

  4. However, the class type T generally doesn't have constructors from volatile T or const volatile T glvalues, and thus neither convertible_to<volatile T&, T> and convertible_to<volatile T&&, T> is satisfied.

Ideally, span should not require any form of construction of element_type. Although usages of class types provided by the standard library via volatile glvalues are generally not supported, I think span<volatile T, E> is still useful for some user-defined class type T, and thus shouldn't be forbidden.

[Kona 2022-11-12; Set priority to 2]

[2025-03-22; Jiang An comments and provides wording]

The proposed resolution relaxes the input_iterator concept (which in turn relaxes stronger iterator concepts) and extends iter_const_reference_t and the exposition only iter-const-rvalue-reference-t aliases. Another approach can be just relaxing the indirectly_readable concept, but its impact may be larger than expected.

Proposed resolution:

This wording is relative to N5008.

  1. Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:

    […]
    // 24.3.4.9 [iterator.concept.input], concept input_iterator
    template<class I>
      concept deref-to-value-t = see below;            // freestanding
    
    template<class I>
      concept weakly-indirectly-readable = see below;  // freestanding
    
    template<class I>
    concept input_iterator = see below;                // freestanding
    
    […]
    
    // 24.5.3.2 [const.iterators.alias], alias templates
    template<indirectly_readableweakly-indirectly-readable I>
      using iter_const_reference_t = see below;
    
  2. Modify 24.3.4.9 [iterator.concept.input], concept input_iterator synopsis, as indicated:

    template<class I>
      concept deref-to-value-t-impl =      // exposition only
        same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>> &&
        is_object_v<iter_value_t<I>>;
    
    template<class I>
      concept deref-to-value-t =           // exposition only
        deref-to-value-t-impl<remove_cvref_t<I>>;
    
    template<class I>
      concept weakly-indirectly-readable = // exposition only
        deref-to-value-t<I> ||
        indirectly_readable<I>;
    
    template<class I>
      concept input_iterator =
        input_or_output_iterator<I> &&
        indirectly_readableweakly-indirectly-readable<I> &&
        requires { typename ITER_CONCEPT(I); } &&
        derived_from<ITER_CONCEPT(I), input_iterator_tag>;
    
  3. Modify 24.5.3.2 [const.iterators.alias] as indicated:

    template<class T>
      using ref-add-const-t = see below;
    

    -?- Result: If is_lvalue_reference_v<T> is true, const remove_reference_t<T>&. Otherwise, if is_rvalue_reference_v<T> is true, const remove_reference_t<T>&&. Otherwise, T.

    template<indirectly_readableweakly-indirectly-readable It>
      using iter_const_reference_t = see below;
        common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>;
    

    -?- Result: If It models deref-to-value-t, ref-add-const-t<iter_reference_t<It>>. Otherwise, common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>.

    [Drafting note: The simple deref-to-value-t case should be detected first, which avoids unnecessary instantiations and IFNDR-ness.]

  4. Modify 24.5.3.3 [const.iterators.iterator] as indicated:

    […]
    template<indirectly_readableweakly-indirectly-readable I>
      using iter-const-rvalue-reference-t =              // exposition only
        common_reference_t<const iter_value_t<I>&&, iter_rvalue_reference_t<I>>;
    […]