3638. vector<bool>::swap(reference, reference) is useless

Section: 23.3.12 [vector.bool] Status: New Submitter: Jonathan Wakely Opened: 2021-11-12 Last modified: 2024-08-21

Priority: 3

View other active issues in [vector.bool].

View all other issues in [vector.bool].

View all issues with New status.

Discussion:

vector<bool> provides a static member function that can be used to swap rvalues of type vector<bool>::reference like so:

vector<bool> v{true, false};
vector<bool>::swap(v[0], v[1]);

This is not useful. Nobody calls swap like that. This fails to make v[0] swappable with v[1] as per 16.4.4.3 [swappable.requirements]. The similar SGI STL bit_vector class that vector<bool> is partially inspired by has a "global function" with the same signature, described as:

"Swaps the bits referred to by x and y. This is a global function, not a member function. It is necessary because the ordinary version of swap takes arguments of type T&, and bit_vector::reference is a class, not a built-in C++ reference."

For some reason this became a static member function of vector<bool> in the C++ standard.

We should restore the intended functionality, and deprecate the useless function.

Previous resolution [SUPERSEDED]:

This wording is relative to N4901.

  1. Create a new subclause [vector.bool.general] below 23.3.12 [vector.bool] and move paragraphs p1-p3 (including the class template vector<bool, Allocator> partial specialization synopsis) into that subclause.

  2. Add to the synopsis in [vector.bool.general] p1 (née 23.3.12 [vector.bool] p1):

    […]
    // bit reference
    class reference {
      friend class vector;
      constexpr reference() noexcept;
    public:
      constexpr reference(const reference&) = default;
      constexpr ~reference();
      constexpr operator bool() const noexcept;
      constexpr reference& operator=(bool x) noexcept;
      constexpr reference& operator=(const reference& x) noexcept;
      constexpr const reference& operator=(bool x) const noexcept;
      constexpr void flip() noexcept; // flips the bit
      
      friend constexpr void swap(reference x, reference y) noexcept;
      friend constexpr void swap(reference x, bool& y) noexcept;
      friend constexpr void swap(bool& x, reference y) noexcept;
    };
    […]
    
  3. Remove the static swap function from the class template vector<bool, Allocator> partial specialization synopsis:

    […]
    constexpr void swap(vector&);
    constexpr static void swap(reference x, reference y) noexcept;
    constexpr void flip() noexcept; // flips all bits
    […]
    
  4. Create a new subclause [vector.bool.ref] after p3, with p4 as its first paragraph, and add after it:

    22.3.12.? Class vector<bool, Allocator>::reference [vector.bool.ref]

    -1- reference is a class that simulates the behavior of references of a single bit in vector<bool>. The conversion function returns true when the bit is set, and false otherwise. The assignment operators set the bit when the argument is (convertible to) true and clear it otherwise. flip reverses the state of the bit.

    constexpr void flip() noexcept;
    

    -?- Effects: *this = !*this;

     friend constexpr void swap(reference x, reference y) noexcept;
     friend constexpr void swap(reference x, bool& y) noexcept;
     friend constexpr void swap(bool& x, reference y) noexcept;
    

    -?- Effects: Exchanges the contents of x and y as if by:

    bool b = x;
    x = y;
    y = b;
    
  5. Create a new subclause [vector.bool.mem] after that, containing the paragraphs describing flip() and the hash specialization:

    22.3.12.? Class vector<bool, Allocator> members [vector.bool.mem]

    constexpr void flip() noexcept;
    

    -1- Effects: Replaces each element in the container with its complement.

     constexpr static void swap(reference x, reference y) noexcept;
    

    -6- Effects: Exchanges the contents of x and y as if by:

    bool b = x;
    x = y;
    y = b;
    
    template<class Allocator> struct hash<vector<bool, Allocator>>;
    

    -7- The specialization is enabled (22.10.19 [unord.hash]).

  6. Create a new subclause [depr.vector.bool.swap] after [depr.string.capacity]

    D.? Deprecated vector<bool, Allocator> swap [depr.vector.bool.swap]

    -?- The following member is declared in addition to those members specified in 23.3.12 [vector.bool]:

    namespace std {
      template<class Allocator> class vector<bool, Allocator> {
      public:
        constexpr static void swap(reference x, reference y) noexcept;
      };
    }
    
    constexpr static void swap(reference x, reference y) noexcept;
    

    -?- Effects: swap(x, y).

[2022-01-22; Jonathan replaces swap(x, y) in the Annex D wording, following reflector discussion about lookup for swap finding itself in that context. ]

[2022-01-30; Reflector poll]

Set priority to 3 after reflector poll.

Previous resolution [SUPERSEDED]:

This wording is relative to N4901.

  1. Create a new subclause [vector.bool.general] below 23.3.12 [vector.bool] and move paragraphs p1-p3 (including the class template vector<bool, Allocator> partial specialization synopsis) into that subclause.

  2. Add to the synopsis in [vector.bool.general] p1 (née 23.3.12 [vector.bool] p1):

    […]
    // bit reference
    class reference {
      friend class vector;
      constexpr reference() noexcept;
    public:
      constexpr reference(const reference&) = default;
      constexpr ~reference();
      constexpr operator bool() const noexcept;
      constexpr reference& operator=(bool x) noexcept;
      constexpr reference& operator=(const reference& x) noexcept;
      constexpr const reference& operator=(bool x) const noexcept;
      constexpr void flip() noexcept; // flips the bit
      
      friend constexpr void swap(reference x, reference y) noexcept;
      friend constexpr void swap(reference x, bool& y) noexcept;
      friend constexpr void swap(bool& x, reference y) noexcept;
    };
    […]
    
  3. Remove the static swap function from the class template vector<bool, Allocator> partial specialization synopsis:

    […]
    constexpr void swap(vector&);
    constexpr static void swap(reference x, reference y) noexcept;
    constexpr void flip() noexcept; // flips all bits
    […]
    
  4. Create a new subclause [vector.bool.ref] after p3, with p4 as its first paragraph, and add after it:

    22.3.12.? Class vector<bool, Allocator>::reference [vector.bool.ref]

    -1- reference is a class that simulates the behavior of references of a single bit in vector<bool>. The conversion function returns true when the bit is set, and false otherwise. The assignment operators set the bit when the argument is (convertible to) true and clear it otherwise. flip reverses the state of the bit.

    constexpr void flip() noexcept;
    

    -?- Effects: *this = !*this;

     friend constexpr void swap(reference x, reference y) noexcept;
     friend constexpr void swap(reference x, bool& y) noexcept;
     friend constexpr void swap(bool& x, reference y) noexcept;
    

    -?- Effects: Exchanges the contents of x and y as if by:

    bool b = x;
    x = y;
    y = b;
    
  5. Create a new subclause [vector.bool.mem] after that, containing the paragraphs describing flip() and the hash specialization:

    22.3.12.? Class vector<bool, Allocator> members [vector.bool.mem]

    constexpr void flip() noexcept;
    

    -1- Effects: Replaces each element in the container with its complement.

     constexpr static void swap(reference x, reference y) noexcept;
    

    -6- Effects: Exchanges the contents of x and y as if by:

    bool b = x;
    x = y;
    y = b;
    
    template<class Allocator> struct hash<vector<bool, Allocator>>;
    

    -7- The specialization is enabled (22.10.19 [unord.hash]).

  6. Create a new subclause [depr.vector.bool.swap] after [depr.string.capacity]

    D.? Deprecated vector<bool, Allocator> swap [depr.vector.bool.swap]

    -?- The following member is declared in addition to those members specified in 23.3.12 [vector.bool]:

    namespace std {
      template<class Allocator> class vector<bool, Allocator> {
      public:
        constexpr static void swap(reference x, reference y) noexcept;
      };
    }
    
    constexpr static void swap(reference x, reference y) noexcept;
    

    -?- Effects: Exchanges the contents of x and y as if by:

    bool b = x;
    x = y;
    y = b;
    

[2024-08-21; Jonathan provides improved wording]

Rebase on the current draft, change "exchanges the contents" to "exchanges the denoted values", and don't split the subclause into new subclauses.

Proposed resolution:

This wording is relative to N4988.

  1. Add to the synopsis in 23.3.12.1 [vector.bool.pspc] p1:

    […]
    // bit reference
    class reference {
      friend class vector;
      constexpr reference() noexcept;
    
    public:
      constexpr reference(const reference&) = default;
      constexpr ~reference();
      constexpr operator bool() const noexcept;
      constexpr reference& operator=(bool x) noexcept;
      constexpr reference& operator=(const reference& x) noexcept;
      constexpr const reference& operator=(bool x) const noexcept;
      constexpr void flip() noexcept;   // flips the bit
      
      friend constexpr void swap(reference x, reference y) noexcept;
      friend constexpr void swap(reference x, bool& y) noexcept;
      friend constexpr void swap(bool& x, reference y) noexcept;
    };
    […]
    
  2. Remove the static swap function from the same synopsis:

    […]
    constexpr void swap(vector&)
      noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
               allocator_traits<Allocator>::is_always_equal::value);
    static constexpr void swap(reference x, reference y) noexcept;
    constexpr void flip() noexcept;    // flips all bits
    constexpr void clear() noexcept;
    […]
    
  3. Modify the paragraphs below the synopsis as shown:

    -4- reference is a class that simulates the behavior of references of a single bit in vector<bool>. The conversion function returns true when the bit is set, and false otherwise. The assignment operators set the bit when the argument is (convertible to) converts to true and clear it otherwise. flip reverses the state of the bit.

    constexpr void reference::flip() noexcept;
    

    -?- Effects: *this = !*this;

    
     constexpr void swap(reference x, reference y) noexcept;
     constexpr void swap(reference x, bool& y) noexcept;
     constexpr void swap(bool& x, reference y) noexcept;
    

    -?- Effects: Exchanges the values denoted by x and y as if by:

    bool b = x;
    x = y;
    y = b;
    
    constexpr void flip() noexcept;
    

    -1- Effects: Replaces each element in the container with its complement.

     constexpr static void swap(reference x, reference y) noexcept;
    

    -6- Effects: Exchanges the contents of x and y as if by:

    bool b = x;
    x = y;
    y = b;
    
    template<class Allocator> struct hash<vector<bool, Allocator>>;
    

    -7- The specialization is enabled (22.10.19 [unord.hash]).

  4. Create a new subclause [depr.vector.bool.swap] after D.19 [depr.format]

    D.? Deprecated vector<bool, Allocator> swap [depr.vector.bool.swap]

    -?- The following member is declared in addition to those members specified in 23.3.12 [vector.bool]:

    namespace std {
      template<class Allocator> class vector<bool, Allocator> {
      public:
        static constexpr void swap(reference x, reference y) noexcept;
      };
    }
    
    static constexpr void swap(reference x, reference y) noexcept;
    

    -?- Effects: Exchanges the values denoted by x and y as if by:

    bool b = x;
    x = y;
    y = b;