4187. bitset::reference should be const-assignable

Section: 22.9.2 [template.bitset] Status: New Submitter: Arthur O'Dwyer Opened: 2024-12-21 Last modified: 2025-02-07

Priority: 3

View other active issues in [template.bitset].

View all other issues in [template.bitset].

View all issues with New status.

Discussion:

LWG 3638, which proposes changes to vector<bool>::reference, is related. Should vector<bool>::reference and bitset<N>::reference behave differently in any respect? I think there's no good reason for them to behave differently, and good technical incentives to permit them to behave the same. We already have implementation divergence: libc++ makes bitset::reference const-assignable, whereas libstdc++ and MS STL do not. This means that libc++'s bitset::reference successfully avoids false positives from Arthur's proposed -Wassign-to-class-rvalue diagnostic, while libstdc++'s does not (See Godbolt).

The proposed resolution applies P2321's approach. We can't just insert const into the existing spec, because ABI. But also, since our goal is consistency with the post-P2321 vector<bool>::reference, we should do the same thing here as P2321, not invent anything novel.

Open questions related to the current P/R:

  1. LWG 3638 proposes to add these three swap overloads to vector<bool>::reference. Should we also, consistently, add them to bitset::reference? I think we should.

    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. Both vector<bool>::reference and bitset::reference right now are specified with

    constexpr reference(const reference&) = default;
    

    which is meaningless because we don't know the data members of reference. So this isn't actually specifying that the constructor is trivial, let alone that it's noexcept. I think we should re-specify both types' copy constructors as simply constexpr and noexcept; and then if we want them to be trivial, we should say so in English prose.

[2025-02-07; Reflector poll]

Set priority to 3 after reflector poll.

"Just const-quality the existing assignment operators." "Would need to change the return type (breaking) or use const_cast (weird)." "And it would be needlessly inconsistent with vector<bool>::reference."

"The swap part belongs in LWG 3638."

Previous resolution [SUPERSEDED]:

This wording is relative to N5001.

  1. Modify 22.9.2.1 [template.bitset.general] as indicated:

    namespace std {
      template<size_t N> class bitset {
      public:
        // bit reference
        class reference {
        public:
          constexpr reference(const reference&) = default;
          constexpr ~reference();
          constexpr reference& operator=(bool x) noexcept;           // for b[i] = x;
          constexpr reference& operator=(const reference&) noexcept; // for b[i] = b[j];
          constexpr const reference& operator=(bool x) const noexcept;
          constexpr bool operator~() const noexcept;                 // flips the bit
          constexpr operator bool() const noexcept;                  // for x = b[i];
          constexpr reference& flip() noexcept;                      // for b[i].flip();
          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;      
        };
        […]
      };
      […]
    }
    

[2025-02-07; Jonathan provides improved wording]

Moved swap changes to LWG 3638.

Proposed resolution:

This wording is relative to N5001.

  1. Modify 22.9.2.1 [template.bitset.general] as indicated:

    namespace std {
      template<size_t N> class bitset {
      public:
        // bit reference
        class reference {
        public:
          constexpr reference(const reference&) = default;
          constexpr ~reference();
          constexpr reference& operator=(bool x) noexcept;           // for b[i] = x;
          constexpr reference& operator=(const reference&) noexcept; // for b[i] = b[j];
          constexpr const reference& operator=(bool x) const noexcept;
          constexpr bool operator~() const noexcept;                 // flips the bit
          constexpr operator bool() const noexcept;                  // for x = b[i];
          constexpr reference& flip() noexcept;                      // for b[i].flip();
        };
        […]
      };
      […]
    }