Title
`bitset::reference` should be const-assignable
Status
new
Section
[template.bitset]
Submitter
Arthur O'Dwyer

Created on 2024-12-21.00:00:00 last changed 3 months ago

Messages

Date: 2025-02-07.22:25:24

Proposed resolution:

This wording is relative to N5001.

  1. Modify [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();
        };
        […]
      };
      […]
    }
    
Date: 2025-02-15.00:00:00

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

Moved `swap` changes to LWG 3638.

Date: 2025-02-15.00:00:00

[ 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."

This wording is relative to N5001.

  1. Modify [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;      
        };
        […]
      };
      […]
    }
    
Date: 2024-12-21.00:00:00

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.

History
Date User Action Args
2025-02-07 22:25:24adminsetmessages: + msg14616
2025-02-07 22:17:52adminsetmessages: + msg14609
2024-12-22 11:35:12adminsetmessages: + msg14531
2024-12-21 00:00:00admincreate