3977. constexpr and noexcept for operators for bitmask types

Section: 16.3.3.3.3 [bitmask.types] Status: New Submitter: Jiang An Opened: 2023-08-19 Last modified: 2024-02-22

Priority: 3

View other active issues in [bitmask.types].

View all other issues in [bitmask.types].

View all issues with New status.

Discussion:

Currently, no operator in 16.3.3.3.3 [bitmask.types]/2 is specified as noexcept, and the compound assignment operators are not specified as constexpr.

Implementations are divergent on this. E.g., MSVC STL consistently marks them constexpr and noexcept (given MSVC STL doesn't support pre-C++14 modes), while libstdc++'s compound assignment operators for match_flag_type are constexpr since C++14 but lack noexcept, and the operators for launch are noexcept but not constexpr.

I think it's better to ensure more consistency be integer types and non-integer bitmask types, i.e., require the compound assignment operators to be constexpr (only available in C++14 and later) and all operators to be noexcept.

[2024-02-22; Reflector poll]

Set priority to 3 after reflector poll in September 2023.

[Jonathan commented]

"The proposed change only affects an example showing a possible way to implement a made-up example type. It doesn't change any requirements on bitmask types, or change anything for any of the bitmask types defined in the standard library. It doesn't say that implementing them without noexcept and constexpr would be invalid. This change has no normative effect and certainly doesn't achieve the stated aim of requiring these assignments to be constexpr and non-throwing."

[Casey agreed]

"We should strike paragraph two completely and write up the actual requirements that a bitmask type is required to meet, but that's a lot of work for someone."

Proposed resolution:

This wording is relative to N4958.

  1. Modify 16.3.3.3.3 [bitmask.types] as indicated:

    -2- The bitmask type bitmask can be written:

    // For exposition only.
    // int_type is an integral type capable of representing all values of the bitmask type.
    enum bitmask : int_type {
      V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, …
    };
    
    inline constexpr bitmask C0(V0);
    inline constexpr bitmask C1(V1);
    inline constexpr bitmask C2(V2);
    inline constexpr bitmask C3(V3);
    
    […]
    
    constexpr bitmask operator&(bitmask X, bitmask Y) noexcept {
      return static_cast<bitmask>(
        static_cast<int_type>(X) & static_cast<int_type>(Y));
    }
    constexpr bitmask operator|(bitmask X, bitmask Y) noexcept {
      return static_cast<bitmask>(
        static_cast<int_type>(X) | static_cast<int_type>(Y));
    }
    constexpr bitmask operator^(bitmask X, bitmask Y) noexcept {
      return static_cast<bitmask>(
        static_cast<int_type>(X) ^ static_cast<int_type>(Y));
    }
    constexpr bitmask operator~(bitmask X) noexcept {
      return static_cast<bitmask>(~static_cast<int_type>(X));
    }
    constexpr bitmask& operator&=(bitmask& X, bitmask Y) noexcept {
      X = X & Y; return X;
    }
    constexpr bitmask& operator|=(bitmask& X, bitmask Y) noexcept {
      X = X | Y; return X;
    }
    constexpr bitmask& operator^=(bitmask& X, bitmask Y) noexcept {
      X = X ^ Y; return X;
    }