This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of LEWG status.

4250. swap overloads for indirect and polymorphic only found by ADL

Section: 20.4.1.7 [indirect.swap], 20.4.2.7 [polymorphic.swap] Status: LEWG Submitter: Jonathan Wakely Opened: 2025-05-01 Last modified: 2025-10-14

Priority: 2

View all issues with LEWG status.

Discussion:

The non-member swap overloads for std::indirect and std::polymorphic are defined as hidden friends, so are only available via ADL. This means that calling std::swap(i1, i2) will always use the generic std::swap instead of the custom overload for std::indirect.

[2025-10-14; Reflector poll]

Set priority to 2 after reflector poll. Status New → LEWG.

This is a broader issue than just std::indirect and std::polymorphic, it also affects std::jthread, std::mdspan, move_only_function, copyable_function, node-handle, inplace_vector, flat_map and co., and maybe more.

Are we comfortable that defining swap as a hidden friend means that the generic std::swap might be suboptimal, and for allocator-aware types might even do something with different effects? e.g. if propagate_on_container_swap is true but propagate_on_container_move_assignment is false.

Proposed resolution:

This wording is relative to N5008.

  1. Modify 20.2.2 [memory.syn] as indicated:

    
    // 20.4.1, class template indirect
    template<class T, class Allocator = allocator<T>>
      class indirect;
    
    template<class T, class Allocator>
    constexpr void swap(indirect<T, Allocator>& lhs, indirect<T, Allocator>& rhs) noexcept(see below);
    
    // 20.4.1.10, hash support
    template<class T, class Alloc> struct hash<indirect<T, Alloc>>;
    
    // 20.4.2, class template polymorphic
    template<class T, class Allocator = allocator<T>>
      class polymorphic;
    
    template<class T, class Allocator>
    constexpr void swap(polymorphic<T, Allocator>& lhs, polymorphic<T, Allocator>& rhs) noexcept(see below);
    
    
  2. Modify 20.4.1.2 [indirect.syn] as indicated:

    
    // 20.4.1.7, swap
    constexpr void swap(indirect& other) noexcept(see below);
    friend constexpr void swap(indirect& lhs, indirect& rhs) noexcept(see below);
    
  3. Modify 20.4.1.7 [indirect.swap] as indicated:

    
    template<class T, class Allocator>
    constexpr void swap(indirect<T, Allocator>& lhs, indirect<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)));
    
    -3- Effects: Equivalent to lhs.swap(rhs).
  4. Modify 20.4.2.2 [polymorphic.syn] as indicated:

    
    // 20.4.2.7, swap
    constexpr void swap(polymorphic& other) noexcept(see below);
    friend constexpr void swap(polymorphic& lhs, polymorphic& rhs) noexcept(see below);
    
  5. Modify 20.4.2.7 [polymorphic.swap] as indicated:

    
    template<class T, class Allocator>
    constexpr void swap(polymorphic<T, Allocator>& lhs, polymorphic<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)));
    
    -3- Effects: Equivalent to lhs.swap(rhs).