C++ IO and Formatting


  • Description: A note on C++ I/O streams, formatting manipulators, std::bind with placeholders, and modern formatting (std::format, std::print)
  • My Notion Note ID: K2A-B1-20
  • Created: 2018-12-27
  • Updated: 2026-02-28
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. std::ios_base::openmode

When opening file streams, you can combine these flags to control how the file is opened:

Flag Meaning
std::ios_base::in Open for reading
std::ios_base::out Open for writing
std::ios_base::app Seek to end of stream before each write (append)
std::ios_base::ate Seek to end of stream immediately after opening
std::ios_base::trunc Discard existing contents when opening
std::ios_base::binary Open in binary mode (no text translation)
#include <fstream>

// Open for reading and writing in binary mode
std::fstream file("data.bin",
    std::ios_base::in | std::ios_base::out | std::ios_base::binary);

// Open for appending (does not truncate existing content)
std::ofstream log("app.log", std::ios_base::app);

// Open for writing, truncating existing content (default for ofstream)
std::ofstream out("output.txt", std::ios_base::out | std::ios_base::trunc);

2. Stream Formatting: std::setw and std::setfill

The <iomanip> header provides manipulators for controlling output formatting. std::setw sets the minimum field width, and std::setfill sets the padding character.

#include <iostream>
#include <iomanip>

int main() {
    int value = 42;

    // Pad with zeros to a width of 9 characters
    std::cout << std::setw(9) << std::setfill('0') << value << std::endl;
    // Output: 000000042

    // Right-align with spaces (default)
    std::cout << std::setw(9) << std::setfill(' ') << value << std::endl;
    // Output:        42

    // Left-align
    std::cout << std::left << std::setw(9) << std::setfill('.') << value << std::endl;
    // Output: 42.......

    return 0;
}

Note: std::setw is reset after each insertion. std::setfill persists until changed.

Other commonly used <iomanip> manipulators:

  1. std::setprecision(n) — Number of digits for floating-point output. Combine with std::fixed or std::scientific to control how n is interpreted.
  2. std::hex, std::oct, std::dec — Integer base for output and input.
  3. std::boolalpha / std::noboolalpha — Print booleans as true/false or 1/0.
  4. std::showbase — Prefix 0x / 0 on hex / octal output.

3. std::bind and std::placeholders

std::bind creates a new callable by binding some arguments of an existing callable to fixed values, while leaving others as placeholders for future arguments.

3.1 std::placeholders

Placeholders (_1, _2, _3, …) mark the positions where future arguments will be substituted. _1 represents the first argument passed to the bound function, _2 the second, and so on. Numbering starts at _1.

3.2 std::bind

std::bind produces a forwarding call wrapper. Calling the wrapper invokes the original function with the bound arguments substituted in.

#include <functional>
#include <iostream>

int sum(int a, int b) {
    return a + b;
}

int main() {
    using namespace std::placeholders;

    // Bind the second argument to 3; _1 is the first argument passed to add_3
    auto add_3 = std::bind(sum, _1, 3);
    std::cout << add_3(4) << std::endl;  // sum(4, 3) = 7

    // Swap argument order
    auto swapped = std::bind(sum, _2, _1);
    std::cout << swapped(10, 20) << std::endl;  // sum(20, 10) = 30

    return 0;
}

3.3 Modern Alternative: Lambdas

In most cases, lambdas are clearer and more flexible than std::bind:

// Equivalent to std::bind(sum, _1, 3)
auto add_3 = [](int x) { return sum(x, 3); };

Lambdas are generally preferred in modern C++ because:

  1. They are easier to read at a glance.
  2. They behave more predictably with overloaded functions (no ambiguity in resolving which overload to bind).
  3. They typically optimize better — the compiler can inline through them, while std::bind often involves type-erased dispatch.
  4. They support generic captures ([](auto x) { ... }) and capture-by-move ([p = std::move(ptr)] { ... }), which std::bind cannot do cleanly.

4. std::format and std::print (C++20/23)

std::format (C++20, <format>) and std::print (C++23, <print>) are the modern alternatives to << and printf.

#include <format>
#include <print>          // C++23

std::string s = std::format("Hello, {}!", "world");        // "Hello, world!"
std::print("count = {}\n", 42);                            // C++23: print to stdout
std::println("widget at ({}, {})", 1, 2);                  // adds a newline
std::format_to(buf, "pid {}", pid);                         // write to an iterator

Compared with the alternatives:

printf << (iostream) std::format
Type-safe No (format-string mismatch is runtime UB) Yes Yes
Custom types Manual Overload << Specialize std::formatter<T>
Speed Fast Slow Fast
Positional args No No Yes ({0}, {1})
Compile-time format check No N/A Yes (C++20)
Locale-independent Yes Mixed Yes (default)

For full coverage of format specifiers, custom formatters, and number/string conversions, see C++ Strings and Text § 5.