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.

4058. std::to_address() should be SFINAE-friendly

Section: 20.2.4 [pointer.conversion] Status: New Submitter: Peter Kasting Opened: 2024-03-13 Last modified: 2024-03-15

Priority: Not Prioritized

View all other issues in [pointer.conversion].

View all issues with New status.

Discussion:

LWG 3545(i) made std::pointer_traits SFINAE-friendly. However, std::to_address is still not required to be SFINAE-friendly.

This requires callers who wish to accept both pointer-like and non-pointer-like types to guard calls with a constraint that reimplements the core logic of to_address, such as the following:


template<typename T>
concept IsPointerLike = requires { typename std::pointer_traits<T>::pointer; }
                         || requires (const T& t) { t.operator->(); };

Making to_address not be SFINAE-friendly was seen as desirable, so std::contiguous_iterator would produce a hard error for types marked with contiguous_iterator_tag that do not properly support to_address. Thus any fix should not regress that unless LWG explicitly decides to do so. Also note that libc++'s current implementation of to_address, which is SFINAE-friendly, does not produce such a hard error.

Proposed resolution:

This wording is relative to N4971.

  1. Modify 20.2.4 [pointer.conversion] as indicated:

    template<class Ptr> constexpr auto to_address(const Ptr& p) noexcept;

    -?- Constraints: Either the expression pointer_traits<Ptr>::to_address(p) is well-formed (see 20.2.3.4 [pointer.traits.optmem]), or the expression p.operator->() is well-formed.

    -3- Returns: pointer_traits<Ptr>::to_address(p) if that expression is well-formed (see 20.2.3.4 [pointer.traits.optmem]), otherwise to_address(p.operator->()).

  2. Modify 24.3.4.14 [iterator.concept.contiguous] as indicated:

    -1- The contiguous_iterator concept provides a guarantee that the denoted elements are stored contiguously in memory.

    
    template<class I>
      concept contiguous_iterator =
        random_access_iterator<I> &&
        derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> &&
        is_lvalue_reference_v<iter_reference_t<I>> &&
        same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&
        requires(const I& i) {
          { to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>;
        }
        std::same_as<decltype([] { return std::to_address(std::declval<I>()); }()),
                     std::add_pointer_t<std::iter_reference_t<I>>>;
    

    [The submitter welcomes less awkward alternatives to achieve the desired hard error for contiguous_iterator.]