logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·30.10.26
← Back to news

The complement of true is true, except when it's false

Original article
dryperspective.github.io|54 points|19 comments|by aw1621107|Oct 4, 2026

The Paradox of the Boolean Complement: When ~true is true (and sometimes not)

By Matthew Taylor | October 1, 2026


Introduction

I recently spent some time analyzing P4313R1, a proposal for the C++26 standard. This paper suggests introducing a set of bitmask operations specifically for enumerations, which developers can opt into using a new annotation: [[=std::bitmask_type]].

To illustrate, consider the following implementation:

Enum MemberValueDescription
None0No permissions
Read1 << 0Read access
Write1 << 1Write access
Execute1 << 2Execute access
enum class [[=std::bitmask_type]] Permission { 
    None = 0, 
    Read = 1 << 0, 
    Write = 1 << 1, 
    Execute = 1 << 2, 
};

The goal of [[=std::bitmask_type]] is to automatically provide the Permission type with bitwise operators, sparing the developer from manually overloading them. This exploration led me down a rabbit hole into the "murky underbelly" of how C++ handles integer operations.

The Mechanics of Integral Promotion

C++ compilers treat bitwise operations as built-in functions for any integral type. This includes types we don't always associate with integers, such as:

  • wchar_t
  • UTF character types (char8_t through char32_t)
  • bool

Crucially, when you perform an operation like a | b on types smaller than an int, the compiler does not operate on the raw bits of those types. Instead, they undergo integral promotion.

Example of Promotion

Consider two short variables:

constexpr short perm_A { 1 << 0 };
constexpr short perm_B { 1 << 1 };

// The result of the bitwise OR is actually an int, not a short
static_assert(std::same_as<decltype(perm_A | perm_B), int>);

Generally, this is benign. If you cast the result back to a short, the extra bytes are truncated, leaving you with the expected value. To avoid "silent narrowing conversions," it is a good practice to use static_cast to return the result to the original type. However, integral promotion is mandatory; the developer cannot opt out.

The Hazard of Boolean Conversion

The real danger arises when combining promotion with boolean conversion. Unlike truncation, boolean conversion follows a strict logic: value=0  ⟹  false\text{value} = 0 \implies \text{false}value=0⟹false value≠0  ⟹  true\text{value} \neq 0 \implies \text{true}value=0⟹true

When a non-zero integer is converted to bool, the original bit pattern is discarded and replaced with the value 1 (representing true).

Assembly Insight

If we take int x{ 10 }; bool b{ static_cast<bool>(x) };, the unoptimized x86-64 GCC 16.2 assembly looks like this:

mov DWORD PTR [rbp-4], 10 →\rightarrow→ Store 10
cmp DWORD PTR [rbp-4], 0 →\rightarrow→ Compare to 0
setne al →\rightarrow→ Set AL to 1 if not equal (non-zero)
mov BYTE PTR [rbp-5], al →\rightarrow→ Store result in bool

As per the standard ([conv.bool]), the result depends solely on whether the value is zero or non-zero.

Putting it Together: The ~true Paradox

What happens when we evaluate ~true and cast it back to bool?

  • true is promoted to int (value: 1).
  • The bitwise NOT (~) is applied. In C++20, two's complement is required, so ~1 becomes -2.
  • The value -2 is cast back to bool.
  • Since −2≠0-2 \neq 0−2=0, it undergoes boolean conversion and becomes true.

Therefore: static_cast<bool>(~true) == true. The complement of true is true.

Application to Enums

Since enums can use bool as an underlying type, we can create a "boolean enum":

enum class [[=std::bitmask_type]] boolean : bool { FALSE, TRUE };

The proposed operator~ in P4313R1 is defined as:

template <bitmask-like T>
constexpr T operator~(T lhs) noexcept {
    return static_cast<T>(~to_underlying(lhs));
}

Following the logic: TRUE →\rightarrow→ bool →\rightarrow→ int →\rightarrow→ ~ →\rightarrow→ int →\rightarrow→ boolean. static_assert(~boolean::TRUE == boolean::TRUE);

When it's False

There is a catch. In some environments, like GCC, the result is different: static_assert(~boolean::TRUE == boolean::TRUE); →\rightarrow→ static_assert(~boolean::TRUE == boolean::FALSE);

To understand why, let's compare two functions in assembly:

enum class boolean : bool { FALSE, TRUE };

void f(int x, bool out) { out = static_cast<bool>(x); }
void g(int x, boolean out) { out = static_cast<boolean>(x); }

Assembly for f(int, bool):

cmp DWORD PTR [rbp-4], 0
setne dl            ; Standard boolean conversion

Assembly for g(int, boolean):

mov eax, DWORD PTR [rbp-4]
and eax, 1          ; Masking the low bit!
mov BYTE PTR [rdx], al

The difference is stark. While f uses setne (any non-zero is true), g uses and eax, 1. This effectively truncates the integer to its least significant bit. Since -2 in binary ends in 0 (...11111110), the and 1 operation results in 0, which is FALSE.

Conclusion: When the compiler treats the cast as a direct assignment to the underlying bool type of an enum rather than a standard boolean conversion, the complement of true finally becomes false.