Skip to content

Operators

Operators are symbols that direct the compiler to perform specific mathematical, logical, or relational manipulations. C++ features a rich array of built-in operators, which can also be customized for user-defined classes.


1. Arithmetic & Assignment Operators

Arithmetic operators perform standard mathematical operations on numeric types.

Operator Name Example Behavior
+ Addition a + b Calculates sum
- Subtraction a - b Calculates difference
* Multiplication a * b Calculates product
/ Division a / b Computes quotient (integer division truncates towards zero)
% Modulo a % b Calculates remainder (only works on integers)
1
2
3
4
5
6
7
8
int a = 11;
int b = 3;

int quotient = a / b;  // Result: 3 (fractional part discarded)
int remainder = a % b; // Result: 2

// To get exact decimal division, at least one operand must be cast to double
double exact = static_cast<double>(a) / b; // Result: 3.66667

Compound Assignment

Compound assignment combines arithmetic operations with value assignment.

1
2
3
int x = 10;
x += 5; // Equivalent to: x = x + 5 (x becomes 15)
x *= 2; // Equivalent to: x = x * 2 (x becomes 30)

2. Increment & Decrement Semantics

Increment (++) and decrement (--) operators increase or decrease a variable's value by 1. They have two forms:

1
2
3
4
5
int x = 5;
int y = ++x; // Prefix form: x is incremented to 6, then y is assigned 6.

int a = 5;
int b = a++; // Postfix form: b is assigned the current value of a (5), then a is incremented to 6.

[!TIP] Performance Recommendation: For custom iterator classes (frequently used in loops), prefer the prefix form (++i) over the postfix form. Postfix creates a temporary copy of the object to return the "old" value before incrementing, incurring a performance penalty.


3. Logical Operators & Short-Circuit Evaluation

Logical operators combine multiple boolean expressions.

Operator Name Behavior
&& Logical AND Returns true only if both operands are true.
|| Logical OR Returns true if at least one operand is true.
! Logical NOT Inverts the boolean value.

Short-Circuit Evaluation

In C++, logical operations are evaluated from left to right. The evaluation stops as soon as the final outcome is guaranteed: - AND (&&): If the left expression is false, the right expression is never evaluated (since the overall result must be false). - OR (||): If the left expression is true, the right expression is never evaluated (since the overall result must be true).

1
2
3
4
5
6
bool check_sensor(); // Expensive function

// If 'is_active' is false, 'check_sensor()' is never called, saving CPU cycles.
if (is_active && check_sensor()) {
    // ...
}

4. Bitwise Operators: Essential for Hardware Interaction

Bitwise operators manipulate data at the individual bit level. This is critical when interfacing with microcontrollers, hardware sensors, and GPIO pins on the Raspberry Pi.

Consider two bytes: a = 5 (0b0000'0101), b = 3 (0b0000'0011).

Operator Name Logic Example Result
& Bitwise AND 1 if both bits are 1 a & b 0b0000'0001 (1)
| Bitwise OR 1 if at least one bit is 1 a | b 0b0000'0111 (7)
^ Bitwise XOR 1 if bits are different a ^ b 0b0000'0110 (6)
~ Bitwise NOT Inverts all bits ~a 0b1111'1010 (-6)
<< Left Shift Shifts bits left, inserts 0 a << 1 0b0000'1010 (10)
>> Right Shift Shifts bits right, drops outer bits a >> 1 0b0000'0010 (2)

GPIO and Register Masking Patterns

#include <iostream>
#include <cstdint>

int main() {
    uint8_t gpio_register = 0b0000'0000; // All pins set to LOW (0)
    const uint8_t PIN_3_MASK = (1 << 3); // Bitmask representing pin 3 (0b0000'1000)

    // 1. Set Pin 3 to HIGH (Using OR)
    gpio_register |= PIN_3_MASK; 
    // gpio_register becomes 0b0000'1000

    // 2. Check if Pin 3 is HIGH (Using AND)
    bool is_pin3_high = (gpio_register & PIN_3_MASK) != 0; 
    // Evaluates to true

    // 3. Toggle Pin 3 (Using XOR)
    gpio_register ^= PIN_3_MASK; 
    // gpio_register goes back to 0b0000'0000

    // 4. Clear Pin 3 to LOW (Using AND with NOT mask)
    gpio_register &= ~PIN_3_MASK; 
    // Guarantees pin 3 is set to 0

    return 0;
}

5. Modern C++20 Three-Way Comparison: The Spaceship Operator (<=>)

Before C++20, to make a custom class support comparison operators (<, <=, >, >=, ==, !=), you had to write six distinct overload functions.

C++20 introduced the Three-Way Comparison Operator, written as <=> and colloquially called the Spaceship Operator.

1
2
3
4
5
6
                           A <=> B
             ┌────────────────┼────────────────┐
             ▼                ▼                ▼
          Result < 0       Result == 0      Result > 0
         (Meaning A < B)  (Meaning A == B) (Meaning A > B)

By defining <=> (and checking for equality), the compiler generates all six comparison operators for you automatically.

#include <iostream>
#include <compare>

struct Version {
    int major;
    int minor;
    int patch;

    // Direct compiler-generated member-by-member comparison
    auto operator<=>(const Version&) const = default;
};

int main() {
    Version v1{1, 2, 0};
    Version v2{1, 3, 1};

    // All comparison operators are automatically generated and work:
    if (v1 < v2) {
        std::cout << "v1 is older than v2\n";
    }
    if (v1 != v2) {
        std::cout << "Versions are different\n";
    }

    return 0;
}

Comparison Categories

The spaceship operator return types describe the strength of the comparison: - std::strong_ordering: Strict equality (e.g. integer comparisons. If a == b, then f(a) == f(b)). - std::weak_ordering: Equivalence where two values compare equal but are distinct (e.g. case-insensitive string comparison). - std::partial_ordering: Unordered values exist (e.g. floats, since NaN compared with anything is unordered).