Document number: P0237R3
Date: 2016-10-17
Project: ISO JTC1/SC22/WG21: Programming Language C++
Audience: LEWG, SG14, SG6
Reply to: Vincent Reverdy <vince.rev@gmail.com>
Robert J. Brunner

Wording for fundamental bit manipulation utilities


Table of Contents


History and feedback


Original wording [February 2016 (pre-Jacksonville) → June 2016 (pre-Oulu)] (P0237R0P0237R1)

The idea of bit manipulation utilities has been originally proposed in the 2016 pre-Jacksonville mailing and has been discussed in both SG6 and LEWG in Jacksonville. The original document, P0237R0 fully describes and discusses the idea of fundamental bit manipulation utilities for the C++ language. Another proposal, P0161R0 had the same motivations but was focusing on bitsets. As P0237R0 seemed more generic and not restrained to the scope of std::bitset, we decided to push forward P0237R0, while still keeping in mind the functionalities and performances allowed by P0161R0. The genericity and abstraction offered by P0237R0 should not introduce a performance overhead (at least with the -O2 optimization level on most compilers) when compared to P0161R0 and N3864.

The feedback from the Jacksonville meeting was positive: in top of their intrinsics functionalities, bit manipulation utilities have the potential to serve as a common basis for a std::dynamic_bitset replacement of std::vector<bool> and for bounded and unbounded precision arithmetic as discussed in N4038.

In terms of design, the following guidance was given by LEWG:

In Jacksonville, the following questions were raised:

These questions can be answered in the following way:

If bit utilities make it to the standard, they will be used, at least, as the basis for an implementation of a dynamic bitset, and are likely to be used as an interface to the underlying containers of words for unbounded and bounded precision arithmetic.

First update [June 2016 (pre-Oulu) → July 2016 (post-Oulu)] (P0237R1P0237R2)

The following polls were taken in LEWG:

The following additional questions were answered in small group dicussions:

The following important design questions were also suggested during the meeting:

As a consequence, the wording has been updated in the following way, compared to P0237R1:

The future revision of this proposal will include assign functions, swap members for std::bit_value, and bitwise operators. Several alternative designs for std::bit_value with a template parameter will also be included.


Second update [July 2016 (post-Oulu) → October 2016 (pre-Issaquah)] (P0237R2P0237R3)

According to the feedback received in Oulu, the following elements have been added or removed:

On top of it, the two following constants were added:


Introduction

This paper proposes a wording for fundamental bit utilities: std::bit_value, std::bit_reference, std::bit_pointer and std::bit_iterator. An in-depth discussion of the motivations and an in-depth exploration of the design space can be found in P0237R0. In short, this paper proposes a set of 4 main utility classes to serve as bit abstractions in order to offer a common and standardized interface for libraries and programs that require bit manipulations. These bit utilities both emulate bits that are easy to use for users, and provide an interface to access the underlying words to implement efficient low-level algorithms.


Examples

An implementation of the fundamental bit utilities is available at https://github.com/vreverdy/bit. Before presenting a wording, we illustrate some of the functionalities of the library.

First, std::bit_value and std::bit_reference:

  // First, we create an integer
  using uint_t = unsigned long long int;
  uint_t intval = 42;                                        // 101010
  
  // Then we create aligned bit values and a bit references on this integer
  std::bit_value bval0(intval);                              // Creates a bit value from the bit at position 0 of intval
  std::bit_reference<uint_t> bref0(intval);                  // Creates a bit reference from the bit at position 0 of intval

  // And unaligned bit values and a bit references on this integer
  std::bit_value bval5(intval, 5);                           // Creates a bit value from the bit at position 5 of intval 
  std::bit_reference<uint_t> bref5(intval, 5);               // Creates a bit reference from the bit at position 5 of intval

  // Display them
  std::cout<<bval0<<bref0<<bval5<<bref5<<std::endl;          // Prints 0011
  
  // Change their values conditionnally
  if (static_cast<bool>(bval5)) {
    bval0.flip();  // Flips the bit without affecting the integer
    bref5.reset(); // Resets the bit to zero and affects the integer
  }
  std::cout<<bval0<<bref0<<bval5<<bref5<<std::endl;          //  Prints 1010
  
  // Prints the location and the corresponding mask of bit references
  std::cout<<bref0.position()<<" "<<bref0.mask()<<std::endl; // Prints 0 and 1
  std::cout<<bref5.position()<<" "<<bref5.mask()<<std::endl; // Prints 5 and 32

Then, with std::bit_pointer:

  // First, we create an array of integers
  using uint_t = unsigned long long int;
  std::array<uint_t, 2> intarr = {42, 314};
  
  // Then we create a bit reference and a bit pointer
  std::bit_reference<uint_t> bref5(intarr[0], 5);            // Creates a bit reference from the bit at position 5 of the first element of the array
  std::bit_pointer<uint_t> bptr(intarr.data());              // Creates a bit pointer from the bit at position 0 of the first element of the array

  // We flip the first bit, and sets the second one to 1 with two methods
  bptr->flip();                                              // Flips the bit
  ++bptr;                                                    // Goes to the next bit
  *bptr.set();                                               // Sets the bit
  
  // Then we advance the bit pointer by more than 64 bits and display its position
  bptr += 71;
  std::cout<<bptr->position()<<std::endl;                    // Prints 7 as the bit is now in the second element of the array
  
  // And finally we set the bit pointer to the position of the bit reference
  bptr = &bref5;

And finally std::bit_iterator, which can serve as a basis of bit algorithm:

  // First, we create a list of integers
  using uint_t = unsigned short int;
  std::list<uint_t> intlst = {40, 41, 42, 43, 44};
  
  // Then we create a pair of aligned bit iterators
  auto bfirst = std::bit_iterator<typename std::list<uint_t>::iterator>(std::begin(intlst));
  auto bend = std::bit_iterator<typename std::list<uint_t>::iterator>(std::end(intlst));

  // Then we count the number of bits set to 1
  auto result = std::count(bfirst, bend, std::bit(1));
  
  // We take a subset of the list
  auto bfirst2 = std::make_bit_iterator(std::begin(intlst), 5);
  auto bend2 = std::make_bit_iterator(std::end(intlst) - 1, 2);
  
  // And we reverse the subset
  std::reverse(bfirst2, bend2);

The count algorithm can be implemented as:

// Counts the number of bits equal to the provided bit value
template <class InputIt, class T> 
typename bit_iterator<InputIt>::difference_type
count(
  bit_iterator<InputIt> first, 
  bit_iterator<InputIt> last, 
  const T& value
)
{
  // Initialization
  using underlying_type = typename bit_iterator<InputIt>::underlying_type;
  using difference_type = typename bit_iterator<InputIt>::difference_type;
  constexpr difference_type digits = binary_digits<underlying_type>::value;
  const bit_value input = value;
  difference_type result = 0;
  auto it = first.base();
    
  // Computation when bits belong to several underlying values
  if (first.base() != last.base()) {
    if (first.position() != 0) {
      result = _popcnt(*first.base() >> first.position());
      ++it;
    }
    for (; it != last.base(); ++it) {
      result += _popcnt(*it);
    }
    if (last.position() != 0) {
      result += _popcnt(*last.base() << (digits - last.position()));
    }
  // Computation when bits belong to the same underlying value
  } else {
    result = _popcnt(_bextr(
      *first.base(), 
      static_cast<underlying_type>(first.position()), 
      static_cast<underlying_type>(last.position() - first.position())
    ));
  }
    
  // Negates when the number of zero bits is requested
  if (!static_cast<bool>(input)) {
    result = (last - first) - result;
  }
    
  // Finalization
  return result;
}

As illustrated in these examples, bit utilities act as a convenient interface between high level code that can use bit manipulation through bit iterators, and low level algorithms that can call dedicated instruction sets and compiler intrinsics.

More information as well as more examples can be found in the presentation From Numerical Cosmology to Efficient Bit Abstractions for the Standard Library that was given during CppCon 2016.


Proposed Wording


Header <bit>

Add the following header to the standard:

<bit>

Helper struct std::binary_digits

Add to the <bit> synopsis:

// Binary digits structure definition
template <class T>
struct binary_digits 
: std::enable_if<
  std::is_integral<T>::value && std::is_unsigned<T>::value,
  std::integral_constant<std::size_t, std::numeric_limits<T>::digits>
>::type
{};
Requires: std::is_integral<UIntType>::value && std::is_unsigned<UIntType>::value.
Remarks: This helper struct allows users to extend the behavior of bit utilities on other types (for example __uint128_t) through their own specializations, independently from std::numeric_limits::digits.

Class std::bit_value

Add to the <bit> synopsis:

// Bit value class definition
class bit_value
{public:
    
  // Types
  using size_type = std::size_t;
    
  // Lifecycle
  bit_value() noexcept = default;
  template <class T> 
  constexpr bit_value(bit_reference<T> val) noexcept;
  template <class UIntType>
  explicit constexpr bit_value(UIntType val) noexcept;
  template <class UIntType> 
  constexpr bit_value(UIntType val, size_type pos);
    
  // Assignment
  template <class T> 
  bit_value& operator=(bit_reference<T> val) noexcept;
  template <class UIntType>
  void assign(UIntType val) noexcept;
  template <class UIntType> 
  void assign(UIntType val, size_type pos);
  
  // Bitwise assignment
  bit_value& operator&=(bit_value other) noexcept;
  bit_value& operator|=(bit_value other) noexcept;
  bit_value& operator^=(bit_value other) noexcept;

  // Conversion
  explicit constexpr operator bool() const noexcept;

  // Swap members
  void swap(bit_value& other);
  template <class T> 
  void swap(bit_reference<T> other);

  // Bit manipulation
  void set(bool b) noexcept;
  void set() noexcept;
  void reset() noexcept;
  void flip() noexcept;
};

// Bitwise operators
constexpr bit_value operator&(bit_value lhs, bit_value rhs) noexcept;
constexpr bit_value operator|(bit_value lhs, bit_value rhs) noexcept;
constexpr bit_value operator^(bit_value lhs, bit_value rhs) noexcept;

// Comparison operators
constexpr bool operator==(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator!=(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator<(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator<=(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator>(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator>=(bit_value lhs, bit_value rhs) noexcept;

// Stream functions
template <class CharT, class Traits>
std::basic_ostream<CharT, Traits>& operator<<(
  std::basic_ostream<CharT, Traits>& os,
  bit_value x
);
template <class CharT, class Traits>
std::basic_istream<CharT, Traits>& operator>>(
  std::basic_istream<CharT, Traits>& is,
  bit_value& x
);

// Constants
static constexpr bit_value zero_bit(0U);
static constexpr bit_value one_bit(1U);

Lifecycle

bit_value() noexcept = default;
Effects: Constructs the bit value without initializing its value.

template <class T> constexpr bit_value(bit_reference<T> val) noexcept;
Effects: Constructs the bit value from the value of a bit reference.

template <class UIntType> explicit constexpr bit_value(UIntType val) noexcept;
Requires: std::binary_digits<UIntType>::value should exist and be strictly positive.
Effects: Constructs the bit value from the bit at position 0 of val as in static_cast<bool>(val & static_cast<UIntType>(1)).

template <class UIntType> constexpr bit_value(UIntType val, size_type pos);
Requires: std::binary_digits<UIntType>::value should exist, be strictly positive and pos should verify pos < std::binary_digits<UIntType>::value, otherwise the behavior is undefined.
Effects: Constructs the bit value from the bit at position pos of val as in static_cast<bool>((val >> pos) & static_cast<UIntType>(1)).
Remarks: For unsigned integral types, static_cast<bool>((val >> pos) & static_cast<UIntType>(1)) is equivalent to static_cast<bool>(val & (static_cast<UIntType>(1) << pos)) for pos < std::binary_digits<UIntType>::value.

Assignment

template <class T> bit_value& operator=(bit_reference<T> val) noexcept;
Effects: Assigns a bit reference to the bit value.
Returns: *this.

template <class UIntType> void assign(UIntType val) noexcept;
Effects: Assigns the bit at position 0 of val as in static_cast<bool>(val & static_cast<UIntType>(1)) to the bit value.

template <class UIntType> void assign(UIntType val, size_type pos);
Requires: std::binary_digits<UIntType>::value should exist, be strictly positive and pos should verify pos < std::binary_digits<UIntType>::value, otherwise the behavior is undefined.
Effects: Assigns the bit at position pos of val as in static_cast<bool>((val >> pos) & static_cast<UIntType>(1)) to the bit value.
Remarks: For unsigned integral types, static_cast<bool>((val >> pos) & static_cast<UIntType>(1)) is equivalent to static_cast<bool>(val & (static_cast<UIntType>(1) << pos)) for pos < std::binary_digits<UIntType>::value.

Bitwise assignment

bit_value& operator&=(bit_value other) noexcept;
bit_value& operator|=(bit_value other) noexcept;
bit_value& operator^=(bit_value other) noexcept;
Effects: Assigns a new value to the bit value by performing a bitwise operation with another bit value.
Returns: *this.

Conversion

explicit constexpr operator bool() const noexcept;
Effects: Explicitly converts the bit value to a boolean value.

Swap members

void swap(bit_value& other);
template <class T> void swap(bit_reference<T> other);
Effects: Swaps the value of the bit value with the value of the other bit.

Bit manipulation

void set(bool b) noexcept;
Effects: Assigns b to the bit value.

void set() noexcept;
Effects: Unconditionally sets the bit value to 1.

void reset() noexcept;
Effects: Unconditionally resets the bit value to 0.

void flip() noexcept;
Effects: Flips the bit value, 0 becoming 1 and 1 becoming 0.

Bitwise operators

constexpr bit_value operator&(bit_value lhs, bit_value rhs) noexcept;
constexpr bit_value operator|(bit_value lhs, bit_value rhs) noexcept;
constexpr bit_value operator^(bit_value lhs, bit_value rhs) noexcept;
Effects: Applies bitwise operators to two single bits.
Returns: The bit value resulting from the operation.
Remarks: Bit references are handled through these operators using implicit conversions to bit values.

Comparison operators

constexpr bool operator==(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator!=(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator<(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator<=(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator>(bit_value lhs, bit_value rhs) noexcept;
constexpr bool operator>=(bit_value lhs, bit_value rhs) noexcept;
Effects: Compares two bit values.
Returns: The boolean value of the comparison.
Remarks: Bit references get compared through these operators using implicit conversions to bit values.

Stream functions

template <class CharT, class Traits> std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, bit_value x);
Effects: Outputs the bit value to the stream in a form equivalent to os << static_cast<int>(static_cast<bool>(x)).
Returns: The updated stream.

template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, bit_value& x);
Effects: Reads the next character from the input stream and sets the bit value accordingly if this caracter is 0 or 1. The behavior is undefined otherwise.
Returns: The updated stream.


Class template std::bit_reference

Add to the <bit> synopsis:

// Bit reference class definition
template <class UIntType>
class bit_reference
{public:
    
  // Types
  using underlying_type = UIntType;
  using size_type = std::size_t;

  // Lifecycle
  template <class T> 
  constexpr bit_reference(const bit_reference<T>& other) noexcept;
  explicit constexpr bit_reference(underlying_type& ref) noexcept;
  constexpr bit_reference(underlying_type& ref, size_type pos);

  // Assignment
  bit_reference& operator=(const bit_reference& other) noexcept;
  template <class T> 
  bit_reference& operator=(const bit_reference<T>& other) noexcept;
  bit_reference& operator=(bit_value val) noexcept;
  void assign(underlying_type val) noexcept;
  void assign(underlying_type val, size_type pos);

  // Bitwise assignment
  bit_reference& operator&=(bit_value other) noexcept;
  bit_reference& operator|=(bit_value other) noexcept;
  bit_reference& operator^=(bit_value other) noexcept;
    
  // Conversion
  explicit constexpr operator bool() const noexcept;

  // Access
  constexpr bit_pointer<UIntType> operator&() const noexcept;
    
  // Swap members
  template <class T> 
  void swap(bit_reference<T> other);
  void swap(bit_value& other);

  // Bit manipulation
  void set(bool b) noexcept;
  void set() noexcept;
  void reset() noexcept;
  void flip() noexcept;

  // Underlying details
  constexpr underlying_type* address() const noexcept;
  constexpr size_type position() const noexcept;
  constexpr underlying_type mask() const noexcept;
};

// Swap and exchange
template <class T, class U>
void swap(bit_reference<T> lhs, bit_reference<U> rhs) noexcept;
template <class T>
void swap(bit_reference<T> lhs, bit_value& rhs) noexcept;
template <class U>
void swap(bit_value& lhs, bit_reference<U> rhs) noexcept;
template <class T, class U = bit_value>
bit_value exchange(bit_reference<T> x, U&& val);

// Stream functions
template <class CharT, class Traits, class T>
std::basic_ostream<CharT, Traits>& operator<<(
  std::basic_ostream<CharT, Traits>& os,
  bit_reference<T> x
);
template <class CharT, class Traits, class T>
std::basic_istream<CharT, Traits>& operator>>(
  std::basic_istream<CharT, Traits>& is,
  bit_reference<T>& x
);
Requires: std::binary_digits<UIntType>::value should exist and be strictly positive.

Lifecycle

template <class T> constexpr bit_reference(const bit_reference<T>& other) noexcept;
Effects: Constructs the bit reference from a bit reference on another type T.
Remarks: The main usage of this constructor is when T is the same as UIntType, but differently cv-qualified.

explicit constexpr bit_reference(underlying_type& ref) noexcept;
Effects: Constructs the bit reference by referencing the bit at position 0 of ref as in static_cast<bool>(ref & static_cast<UIntType>(1)).

constexpr bit_reference(underlying_type& ref, size_type pos);
Requires: pos should verify pos < std::binary_digits<UIntType>::value, otherwise the behavior is undefined.
Effects: Constructs the bit reference by referencing the bit at position pos of ref as in static_cast<bool>((ref >> pos) & static_cast<UIntType>(1)).
Remarks: For unsigned integral types, static_cast<bool>((ref >> pos) & static_cast<UIntType>(1)) is equivalent to static_cast<bool>(ref & (static_cast<UIntType>(1) << pos)) for pos < std::binary_digits<UIntType>::value.

Assignment

bit_reference& operator=(const bit_reference& other) noexcept;
Effects: Copy assigns from the value of the bit referenced in other.
Returns: *this.
Remarks: The behavior is different than what a default copy assignment would be: the value of the other bit is copied, not the reference itself.

template <class T> bit_reference& operator=(const bit_reference<T>& other) noexcept;
Effects: Copy assigns from the value of the bit referenced in other.
Returns: *this. Remarks: The main usage of this assignment operator is when T is the same as UIntType, but differently cv-qualified.

bit_reference& operator=(bit_value val) noexcept;
Effects: Assigns the value of the bit val to the referenced bit.
Returns: *this.

void assign(underlying_type val) noexcept;
Effects: Assigns the bit at position 0 of val as in static_cast<bool>(val & static_cast<UIntType>(1)) to the referenced bit.

void assign(underlying_type val, size_type pos);
Requires: pos should verify pos < std::binary_digits<UIntType>::value, otherwise the behavior is undefined.
Effects: Assigns the bit at position pos of ref as in static_cast<bool>((ref >> pos) & static_cast<UIntType>(1)) to the bit reference.
Remarks: For unsigned integral types, static_cast<bool>((ref >> pos) & static_cast<UIntType>(1)) is equivalent to static_cast<bool>(ref & (static_cast<UIntType>(1) << pos)) for pos < std::binary_digits<UIntType>::value.

Bitwise assignment

bit_reference& operator&=(bit_value other) noexcept;
bit_reference& operator|=(bit_value other) noexcept;
bit_reference& operator^=(bit_value other) noexcept;
Effects: Assigns a new value to the bit reference by performing a bitwise operation with a bit value.
Returns: *this.

Conversion

explicit constexpr operator bool() const noexcept;
Effects: Explicitly converts the bit value to a boolean value.

Access

constexpr bit_pointer<UIntType> operator&() const noexcept;
Returns: A bit pointer pointing to the referenced bit.

Swap members

template <class T> void swap(bit_reference<T> other);
void swap(bit_value& other);
Effects: Swaps the value of the referenced bit with the value of the other bit.

Bit manipulation

void set(bool b) noexcept;
Effects: Assigns b to the referenced bit.

void set() noexcept;
Effects: Unconditionally sets the referenced bit to 1.

void reset() noexcept;
Effects: Unconditionally resets the referenced bit to 0.

void flip() noexcept;
Effects: Flips the referenced bit, 0 becoming 1 and 1 becoming 0.

Underlying details

constexpr underlying_type* address() const noexcept;
Returns: A pointer to the object in which the referenced bit is.

constexpr size_type position() const noexcept;
Returns: The position of the referenced bit in the underlying object.
Remarks: The position verifies pos >= 0 and pos < std::binary_digits<UIntType>::value.

constexpr underlying_type mask() const noexcept;
Returns: A mask of underlying_type in which the bit at position pos is set to 1. The mask is equivalent to static_cast<UIntType>(1) << pos.

Swap and exchange

template <class T, class U> void swap(bit_reference<T> lhs, bit_reference<U> rhs) noexcept;
template <class T> void swap(bit_reference<T> lhs, bit_value& rhs) noexcept;
template <class U> void swap(bit_value& lhs, bit_reference<U> rhs) noexcept;
Effects: Swaps the value of the passed bits.

template <class T, class U = bit_value> bit_value exchange(bit_reference<T> x, U&& val);
Effects: Replaces the value of x with val and returns the old value of x.
Remarks: This function is an overload of std::exchange so that it performs the correct operation when operating on bits.

Stream functions

template <class CharT, class Traits, class T> std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, bit_reference<T> x);
Effects: Outputs the referenced bit to the stream in a form equivalent to os << static_cast<int>(static_cast<bool>(x)).
Returns: The updated stream.

template <class CharT, class Traits, class T> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, bit_reference<T>& x);
Effects: Reads the next character from the input stream and sets the referenced bit accordingly if this caracter is 0 or 1. The behavior is undefined otherwise.
Returns: The updated stream.


Class template std::bit_pointer

Add to the <bit> synopsis:

// Bit pointer class definition
template <class UIntType>
class bit_pointer
{public:
    
  // Types
  using underlying_type = UIntType;
  using size_type = std::size_t;
  using difference_type = /* Implementation defined, at least std::ptrdiff_t */;

  // Lifecycle
  bit_pointer() noexcept = default;
  template <class T> 
  constexpr bit_pointer(const bit_pointer<T>& other) noexcept;
  constexpr bit_pointer(std::nullptr_t) noexcept;
  explicit constexpr bit_pointer(underlying_type* ptr) noexcept;
  constexpr bit_pointer(underlying_type* ptr, size_type pos);
    
  // Assignment
  bit_pointer& operator=(std::nullptr_t) noexcept;
  bit_pointer& operator=(const bit_pointer& other) noexcept;
  template <class T> 
  bit_pointer& operator=(const bit_pointer<T>& other) noexcept;
    
  // Conversion
  explicit constexpr operator bool() const noexcept;

  // Access
  constexpr bit_reference<UIntType> operator*() const noexcept;
  constexpr bit_reference<UIntType>* operator->() const noexcept;
  constexpr bit_reference<UIntType> operator[](difference_type n) const;
    
  // Increment and decrement operators
  bit_pointer& operator++();
  bit_pointer& operator--();
  bit_pointer operator++(int);
  bit_pointer operator--(int);
  constexpr bit_pointer operator+(difference_type n) const;
  constexpr bit_pointer operator-(difference_type n) const;
  bit_pointer& operator+=(difference_type n);
  bit_pointer& operator-=(difference_type n);
};

// Non-member arithmetic operators
template <class T>
constexpr bit_pointer<T> operator+(
  typename bit_pointer<T>::difference_type n,
  bit_pointer<T> x
);
template <class T, class U>
constexpr typename std::common_type<
  typename bit_pointer<T>::difference_type,
  typename bit_pointer<U>::difference_type
>::type operator-(
  bit_pointer<T> lhs,
  bit_pointer<U> rhs
);

// Comparison operators
template <class T, class U>
constexpr bool operator==(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U>
constexpr bool operator!=(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U>
constexpr bool operator<(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U>
constexpr bool operator<=(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U>
constexpr bool operator>(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U>
constexpr bool operator>=(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
Requires: std::binary_digits<UIntType>::value should exist and be strictly positive.
Remarks: In the class definition above, difference_type is implementation defined but is required to be a signed integer of at least std::ptrdiff_t's size.

Lifecycle

bit_pointer() noexcept = default;
Effects: Constructs the bit pointer leaving it uninitialized.

template <class T> constexpr bit_pointer(const bit_pointer<T>& other) noexcept;
Effects: Constructs the bit pointer from a bit pointer on another type T.
Remarks: The main usage of this constructor is when T is the same as UIntType, but differently cv-qualified.

constexpr bit_pointer(std::nullptr_t) noexcept;
Effects: Constructs a null bit pointer with position 0.

explicit constexpr bit_pointer(underlying_type* ptr) noexcept;
Effects: Constructs the bit pointer by pointing to the bit at position 0 of ptr as in static_cast<bool>(*ptr & static_cast<UIntType>(1)).

constexpr bit_pointer(underlying_type* ptr, size_type pos);
Requires: pos should verify pos < std::binary_digits<UIntType>::value, otherwise the behavior is undefined.
Effects: Constructs the bit pointer by pointing to the bit at position pos of *ptr as in static_cast<bool>((*ptr >> pos) & static_cast<UIntType>(1)).
Remarks: For unsigned integral types, static_cast<bool>((*ptr >> pos) & static_cast<UIntType>(1)) is equivalent to static_cast<bool>(*ptr & (static_cast<UIntType>(1) << pos)) for pos < std::binary_digits<UIntType>::value.

Assignment

bit_pointer& operator=(std::nullptr_t) noexcept;
Effects: Sets the bit pointer to a null bit pointer with position 0.
Returns: *this.

bit_pointer& operator=(const bit_pointer& other) noexcept;
Effects: Copy assigns from the other bit pointer.
Returns: *this.

template <class T> bit_pointer& operator=(const bit_pointer<T>& other) noexcept;
Effects: Copy assigns from the other bit pointer.
Returns: *this.

Conversion

explicit constexpr operator bool() const noexcept;
Effects: Returns false if the underlying pointer is null and the position is 0, returns true otherwise.

Access

constexpr bit_reference<UIntType> operator*() const noexcept;
Returns: A bit reference referencing the pointed bit.

constexpr bit_reference<UIntType>* operator->() const noexcept;
Returns: A pointer to a bit reference referencing the pointed bit.

constexpr bit_reference<UIntType> operator[](difference_type n) const;
Returns: A bit reference referencing the n-th bit after the pointed bit.
Remarks: In that context, if pos + 1 < std::binary_digits<UIntType>::value, the next bit is the bit at pos + 1 in the same underlying object, but if pos + 1 == std::binary_digits<UIntType>::value, the next bit is the bit at position 0 of the next underlying object in memory.

Increment and decrement operators

bit_pointer& operator++();
bit_pointer& operator--();
bit_pointer operator++(int);
bit_pointer operator--(int);
constexpr bit_pointer operator+(difference_type n) const;
constexpr bit_pointer operator-(difference_type n) const;
bit_pointer& operator+=(difference_type n);
bit_pointer& operator-=(difference_type n);
Effects: Performs usual increment and decrement operations.
Remarks: In that context, if pos + 1 < std::binary_digits<UIntType>::value, the next bit is the bit at pos + 1 in the same underlying object, but if pos + 1 == std::binary_digits<UIntType>::value, the next bit is the bit at position 0 of the next underlying object in memory.

Non-member arithmetic operators

template <class T> constexpr bit_pointer<T> operator+(typename bit_pointer<T>::difference_type n, bit_pointer<T> x);
Returns: A bit pointer to the x + n bit.

template <class T, class U> constexpr typename std::common_type< typename bit_pointer<T>::difference_type, typename bit_pointer<U>::difference_type >::type operator-(bit_pointer<T> lhs, bit_pointer<U> rhs);
Returns: The number of bit pointer increments n so that rhs + n == lhs.

Comparison operators

template <class T, class U> constexpr bool operator==(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U> constexpr bool operator!=(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U> constexpr bool operator<(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U> constexpr bool operator<=(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U> constexpr bool operator>(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
template <class T, class U> constexpr bool operator>=(bit_pointer<T> lhs, bit_pointer<U> rhs) noexcept;
Effects: Compares two bit pointers by comparing the pointer to the underlying object first, and the position of the bit in that object if the two underlying pointers are equal.
Returns: The boolean value of the comparison.


Class template std::bit_iterator

Add to the <bit> synopsis:

// Bit iterator class definition
template <class Iterator>
class bit_iterator
{public:
  
  // Types
  using iterator_type = Iterator;
  using underlying_type = typename _cv_iterator_traits<Iterator>::value_type;
  using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
  using value_type = bit_value;
  using difference_type = /* Implementation defined, at least std::ptrdiff_t */;
  using pointer = bit_pointer<underlying_type>;
  using reference = bit_reference<underlying_type>;
  using size_type = std::size_t;

  // Lifecycle
  constexpr bit_iterator();
  template <class T> 
  constexpr bit_iterator(const bit_iterator<T>& other);
  explicit constexpr bit_iterator(iterator_type i);
  constexpr bit_iterator(iterator_type i, size_type pos);

  // Assignment
  template <class T>
  bit_iterator& operator=(const bit_iterator<T>& other);

  // Access
  constexpr reference operator*() const noexcept;
  constexpr pointer operator->() const noexcept;
  constexpr reference operator[](difference_type n) const;

  // Increment and decrement operators
  bit_iterator& operator++();
  bit_iterator& operator--();
  bit_iterator operator++(int);
  bit_iterator operator--(int);
  constexpr bit_iterator operator+(difference_type n) const;
  constexpr bit_iterator operator-(difference_type n) const;
  bit_iterator& operator+=(difference_type n);
  bit_iterator& operator-=(difference_type n);

  // Underlying details
  constexpr iterator_type base() const;
  constexpr size_type position() const noexcept;
  constexpr underlying_type mask() const noexcept;
};

// Non-member arithmetic operators
template <class T>
constexpr bit_iterator<T> operator+(
  typename bit_iterator<T>::difference_type n,
  const bit_iterator<T>& i
);
template <class T, class U>
constexpr typename std::common_type<
  typename bit_iterator<T>::difference_type,
  typename bit_iterator<U>::difference_type
>::type operator-(
  const bit_iterator<T>& lhs,
  const bit_iterator<U>& rhs
);

// Comparison operators
template <class T, class U>
constexpr bool operator==(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U>
constexpr bool operator!=(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U>
constexpr bool operator<(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U>
constexpr bool operator<=(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U>
constexpr bool operator>(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U>
constexpr bool operator>=(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
Requires: std::binary_digits<std::iterator_traits<Iterator>::value_type>::value should exist and be strictly positive.
Remarks: In the class definition above, _cv_iterator_traits is an internal helper struct (for illustration purpose only) whose value_type preserves cv-qualifiers, contrarily to std::iterator_traits. Also, difference_type is implementation defined but is required to be a signed integer of at least std::ptrdiff_t's size.

Lifecycle

constexpr bit_iterator();
Effects: Value initializes the underlying iterator. Iterator operations applied to the resulting iterator have defined behavior if and only if the corresponding operations are defined on a value-initialized iterator of type Iterator.

template <class T> constexpr bit_iterator(const bit_iterator<T>& other);
Effects: Constructs the bit iterator from a bit iterator on another type T.

explicit constexpr bit_iterator(iterator_type i);
Effects: Constructs the bit iterator from an iterator assuming a bit position 0.

constexpr bit_iterator(iterator_type i, size_type pos);
Requires: pos should verify pos < std::binary_digits<underlying_type>::value, otherwise the behavior is undefined.
Effects: Constructs the bit iterator by pointing to the bit at position pos of *it as in static_cast<bool>((*it >> pos) & static_cast<underlying_type>(1)).
Remarks: For unsigned integral types, static_cast<bool>((*it >> pos) & static_cast<underlying_type>(1)) is equivalent to static_cast<bool>(*it & (static_cast<underlying_type>(1) << pos)) for pos < std::binary_digits<underlying_type>::value.

Assignment

template <class T> bit_iterator& operator=(const bit_iterator<T>& other);
Effects: Copy assigns from the other bit iterator.
Returns: *this.

Access

constexpr reference operator*() const noexcept;
Returns: A bit reference referencing the bit corresponding to the current bit iterator.

constexpr pointer operator->() const noexcept;
Returns: A pointer to a bit reference referencing the bit corresponding to the current bit iterator.

constexpr reference operator[](difference_type n) const;
Returns: A bit reference referencing the n-th bit after the bit corresponding to the current bit iterator.
Remarks: In that context, if pos + 1 < std::binary_digits<underlying_type>::value, the next bit is the bit at pos + 1 in the same underlying object, but if pos + 1 == std::binary_digits<underlying_type>::value, the next bit is the bit at position 0 of the next iterator.

Increment and decrement operators

bit_iterator& operator++();
bit_iterator& operator--();
bit_iterator operator++(int);
bit_iterator operator--(int);
constexpr bit_iterator operator+(difference_type n) const;
constexpr bit_iterator operator-(difference_type n) const;
bit_iterator& operator+=(difference_type n);
bit_iterator& operator-=(difference_type n);
Effects: Performs usual increment and decrement operations.
Remarks: In that context, if pos + 1 < std::binary_digits<underlying_type>::value, the next bit is the bit at pos + 1 in the same underlying object, but if pos + 1 == std::binary_digits<underlying_type>::value, the next bit is the bit at position 0 of the next iterator.

Underlying details

constexpr iterator_type base() const;
Returns: An iterator to the object in which the referenced bit is.

constexpr size_type position() const noexcept;
Returns: The position of the referenced bit in the underlying object.
Remarks: The position verifies pos >= 0 and pos < std::binary_digits<underlying_type>::value.

constexpr underlying_type mask() const noexcept;
Returns: A mask of underlying_type in which the bit at position pos is set to 1. The mask is equivalent to static_cast<underlying_type>(1) << pos.

Non-member arithmetic operators

template <class T> constexpr bit_iterator<T> operator+(typename bit_iterator<T>::difference_type n, const bit_iterator<T>& i);
Returns: A bit iterator to the x + n bit.

template <class T, class U> constexpr typename std::common_type<typename bit_iterator<T>::difference_type, typename bit_iterator<U>::difference_type>::type operator-(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
Returns: The number of bit iterator increments n so that rhs + n == lhs.

Comparison operators

template <class T, class U> constexpr bool operator==(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U> constexpr bool operator!=(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U> constexpr bool operator<(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U> constexpr bool operator<=(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U> constexpr bool operator>(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
template <class T, class U> constexpr bool operator>=(const bit_iterator<T>& lhs, const bit_iterator<U>& rhs);
Effects: Compares two bit iterator by comparing the iterator to the underlying object first, and the position of the bit in that object if the two underlying iterators are equal.
Returns: The boolean value of the comparison.


Design questions

The following minor questions will need to be answered:

Additionally, a major design question has been raised during the Oulu meeting: should std::bit_value be a template class? The major advantage would be to allow implicit conversions from std::bit_value& to std::bit_reference, and from std::bit_value* to std::bit_pointer. Three main possibilities should be considered and discussed:

For the two last options, the design becomes more complex. Two questions are particularly crucial: We expect to answer these important design questions during the Issaquah meeting.


Acknowledgements

The authors would like to thank Tomasz Kaminski, Lawrence Crowl, Howard Hinnant, Jens Maurer, Tony Van Eerd, Klemens Morgenstern, Vicente Botet Escriba, Odin Holmes and the other contributors of the ISO C++ Standard - Discussion and of the ISO C++ Standard - Future Proposals groups for their initial reviews and comments.

Vincent Reverdy and Robert J. Brunner have been supported by the National Science Foundation Grant AST-1313415. Robert J. Brunner has been supported in part by the Center for Advanced Studies at the University of Illinois.