Ch2.6: Operators
What Are Operators?
In C++, an operator is a symbol that performs an action on one or more objects.
Operators include arithmetic (+, -, *, /, %), relational (<, >, ==), logical (&&, ||, !), assignment (=, +=, etc.), and more.
Operators work on objects, not “variables.”
Arithmetic Operators with ::std::size_t
#include <fast_io.h>
#include <cstddef>
int main()
{
using namespace ::fast_io::iomnp;
::std::size_t a{10};
::std::size_t b{3};
println("a + b = ", a + b); // 13
println("a - b = ", a - b); // 7
println("a * b = ", a * b); // 30
println("a / b = ", a / b); // 3
println("a % b = ", a % b); // 1
}
The Modulo Operator %
The % operator computes the remainder after division.
With unsigned types, the result is straightforward.
With signed types, the remainder has the same sign as the dividend (left operand).
#include <fast_io.h>
#include <cstdint>
int main()
{
using namespace ::fast_io::iomnp;
::std::int32_t x = 10;
::std::int32_t y = -3;
println("10 % 3 = ", x % 3); // 1
println("10 % -3 = ", x % y); // 1 (same sign as 10)
println("-10 % 3 = ", -x % 3); // -1 (same sign as -10)
}
Overflow and Wrapping
With signed integer types like ::std::int32_t, arithmetic overflow is undefined behavior (UB).
With unsigned types like ::std::uint32_t or ::std::size_t, arithmetic wraps around modulo 2N, which is well-defined.
#include <fast_io.h>
#include <cstdint>
int main()
{
using namespace ::fast_io::iomnp;
// Signed overflow: UB (do not rely on this!)
::std::int32_t si = 2147483647; // max 32-bit signed
// si = si + 1; // UB: signed overflow
// Unsigned wrapping: well-defined
::std::uint32_t ui = 4294967295u; // max 32-bit unsigned
println("ui = ", ui); // 4294967295
ui = ui + 1; // wraps to 0
println("ui after wrap = ", ui); // 0
}
Unary Plus and Unary Minus
The unary plus (+a) and unary minus (-a) operators act on a single operand.
They are different from the binary + and - operators, which require both a left and right operand.
+asimply yields the value ofaunchanged. It is rarely used explicitly.-aproduces the additive inverse ofa(negation). For signed integers, this flips the sign.
Unary Plus and Unary Minus
The unary plus (+a) and unary minus (-a) operators act on a single operand.
They are different from the binary + and - operators, which require both a left and right operand.
+asimply yields the value ofaunchanged. It is rarely used explicitly.-aproduces the additive inverse ofa(negation). For signed integers, this flips the sign.
#include <fast_io.h>
#include <cstdint>
int main()
{
using namespace ::fast_io::iomnp;
::std::int32_t x{42};
println("+x = ", +x); // 42 (unary plus does nothing)
println("-x = ", -x); // -42 (unary minus negates the value)
// With unsigned types, -a wraps modulo 2^N
::std::size_t u{5};
println("-u = ", -u); // wraps around (large value depending on size_t width)
// Special case: INT_LEAST32_MIN
::std::int_least32_t mn{INT_LEAST32_MIN};
// println(-mn); // UB: negating INT_LEAST32_MIN cannot be represented in 32-bit signed
}
Key takeaway: Unary plus is effectively a no‑op, while unary minus negates the operand.
For unsigned types, negation is performed modulo 2N, so -u results in wrapping rather than a true negative number.
For signed types, beware of INT_LEAST32_MIN: its absolute value cannot be represented in 32 bits, so -INT_LEAST32_MIN is undefined behavior.
Special Case: INT_LEAST32_MIN
The constant INT_LEAST32_MIN represents the smallest value that can be stored in a
::std::int_least32_t. On most systems this is -2147483648.
Negating this value with the unary minus operator (-mn) is a dangerous edge case:
- Mathematically,
-(-2147483648)would be+2147483648. - But
::std::int_least32_tcan only represent values up to+2147483647. - This means the result cannot be represented, and in C++ this is undefined behavior (UB).
- Use a wider type, e.g.
::std::int64_t safe = -static_cast<::std::int64_t>(mn);— this will be explained in later chapters.
#include <fast_io.h>
#include <cstdint>
int main()
{
using namespace ::fast_io::iomnp;
::std::int_least32_t mn{INT_LEAST32_MIN};
println("mn = ", mn); // prints -2147483648
// println(-mn); // UB: cannot represent +2147483648 in 32-bit signed
}
Key takeaway: Negating INT_LEAST32_MIN in a 32-bit signed type is undefined behavior.
The safe solution is to use a wider type or an unsigned type, but the details of these strategies will be covered later in this tutorial.
Increment and Decrement
C++ provides ++ and -- operators.
We should prefer ++i (pre-increment) over i++ (post-increment), because i++ creates a temporary copy and is less efficient.
#include <fast_io.h>
#include <cstddef>
int main()
{
using namespace ::fast_io::iomnp;
::std::size_t i{5};
println("i = ", i); // 5
++i; // pre-increment
println("++i = ", i); // 6
// Post-increment pseudo-code:
// auto temp = i;
// ++i;
// return temp;
i = 5;
println("i++ returns ", i++); // prints 5 (old value)
println("i after i++ = ", i); // prints 6
}
Comparison Operators
#include <fast_io.h>
#include <cstddef>
int main()
{
using namespace ::fast_io::iomnp;
::std::size_t a{10};
::std::size_t b{20};
println("a == b: ", a == b); // 0
println("a != b: ", a != b); // 1
println("a < b: ", a < b); // 1
println("a > b: ", a > b); // 0
}
The Spaceship Operator <=>
The spaceship operator (<=>) performs a three-way comparison.
It returns an ordering result: less, equal, or greater.
With fast_io, you can directly print the result as <, =, or >.
#include <fast_io.h>
#include <compare>
#include <cstddef>
int main()
{
using namespace ::fast_io::iomnp;
::std::size_t a{10};
::std::size_t b{20};
auto result = (a <=> b);
println(result); // fast_io prints < (since a < b)
a = 20;
b = 20;
println(a <=> b); // prints =
a = 30;
b = 20;
println(a <=> b); // prints >
}
The Conditional Operator ?:
The conditional operator (?:) chooses between two expressions based on a condition.
It is sometimes called the “ternary operator.”
#include <fast_io.h>
#include <cstddef>
int main()
{
using namespace ::fast_io::iomnp;
::std::size_t a{10};
::std::size_t b{20};
::std::size_t max = (a > b) ? a : b;
println("max = ", max); // 20
}
Operands and Precedence
Every operator acts on one or more operands.
In a binary operator expression like a + b, a is the left operand and b is the right operand.
Unary operators like ++i act on a single operand.
Operators also have precedence, which determines the order in which operations are evaluated.
Parentheses () have the highest precedence and can be used to explicitly control evaluation order.
For the full precedence table, see
cppreference: Operator Precedence
.
#include <fast_io.h>
#include <cstddef>
int main()
{
using namespace ::fast_io::iomnp;
::std::size_t a{2};
::std::size_t b{3};
::std::size_t c{4};
println("a + b * c = ", a + b * c); // multiplication first → 14
println("(a + b) * c = ", (a + b) * c); // parentheses force addition first → 20
}
Summary Table
| Category | Operators | Example | Notes |
|---|---|---|---|
| Arithmetic | + - * / % |
a + b |
Basic math on objects. |
| Modulo | % |
-10 % 3 |
Remainder has same sign as dividend. |
| Comparison | == != < > <= >= |
a < b |
Return boolean results. |
| Spaceship | <=> |
println(a <=> b) |
Prints <, =, or > with fast_io. |
| Logical | && || ! |
x && y |
Work with boolean objects. |
| Assignment | = += -= *= /= |
a += 5 |
Combine arithmetic with assignment. |
| Increment/Decrement | ++ -- |
++i |
Prefer pre-increment ++i over post-increment i++. |
| Conditional | ?: |
(a > b) ? a : b |
Ternary operator chooses between two values. |
| Overflow | — | ::std::int32_t |
Signed overflow is UB; unsigned wraps modulo 2N. |
| Precedence | () |
(a + b) * c |
Parentheses have highest precedence. |