Formatting Text with std::format

C++20's std::format builds strings from a template with {} placeholders — type-safe like streams, concise like printf, and without sticky stream state. It's the modern way to format numbers, align tables and build messages.

Basic use

C++
#include <format>
#include <iostream>
#include <string>

int main() {
    std::string name = "Ada";
    int age = 36;
    double height = 1.74;
    std::string s = std::format("{} is {} years old and {} m tall", name, age, height);
    std::cout << s << '\n';
    std::cout << std::format("{1} before {0}", "second", "first") << '\n';   // positional arguments
}
Output
Ada is 36 years old and 1.74 m tall
first before second

Each {} takes the next argument; {0}, {1} pick arguments by position. Format strings are checked at compile time — a placeholder without an argument is a compile error.

Format specifications

After a colon inside the braces, a small language controls the output: {:[fill][align][sign][width][.precision][type]}.

C++
#include <format>
#include <iostream>
#include <numbers>

int main() {
    double pi = std::numbers::pi;
    std::cout << std::format("[{:.2f}] [{:10.3f}] [{:<10.1f}] [{:^10.1f}]\n", pi, pi, pi, pi);
    std::cout << std::format("[{:5}] [{:<5}] [{:05}] [{:+}] [{:*^9}]\n", 42, 42, 42, 42, 42);
    std::cout << std::format("hex {:x}, HEX {:#X}, octal {:o}, binary {:b}, {:08b}\n", 255, 255, 8, 5, 5);
    std::cout << std::format("{:e} {:g} {:.3g}\n", 123456.789, 0.0001234, 123456.789);
    std::cout << std::format("{:>8} | {:<8} | {:^8}|\n", "right", "left", "mid");
}
Output
[3.14] [     3.142] [3.1       ] [   3.1    ]
[   42] [42   ] [00042] [+42] [***42****]
hex ff, HEX 0XFF, octal 10, binary 101, 00000101
1.234568e+05 0.0001234 1.23e+05
   right | left     |   mid   |
SpecMeaning
< > ^Align left, right, centre (numbers default right, text left)
A character before the alignmentFill character, e.g. {:*^9}
+Always show the sign
0 before the widthPad numbers with zeros
.nPrecision: decimals for f, significant digits for g, max characters for text
f e gFixed, scientific, general floating-point form
d x X o bDecimal, hex, upper hex, octal, binary (# adds 0x/0b)

Tables

C++
#include <format>
#include <iostream>
#include <string>
#include <vector>

struct Line {
    std::string item;
    int qty;
    double price;
};

int main() {
    std::vector<Line> lines{{"Notebook", 4, 3.5}, {"Pen", 10, 1.2}, {"Backpack", 1, 39.9}};
    std::cout << std::format("{:<10} {:>4} {:>8} {:>9}\n", "Item", "Qty", "Price", "Total");
    double sum = 0;
    for (const auto& [item, qty, price] : lines) {
        std::cout << std::format("{:<10} {:>4} {:>8.2f} {:>9.2f}\n", item, qty, price, qty * price);
        sum += qty * price;
    }
    std::cout << std::format("{:-<34}\n{:<24} {:>9.2f}\n", "", "Total", sum);
}
Output
Item        Qty    Price     Total
Notebook      4     3.50     14.00
Pen          10     1.20     12.00
Backpack      1    39.90     39.90
----------------------------------
Total                        65.90

Dynamic width and precision

C++
#include <format>
#include <iostream>

int main() {
    for (int width = 4; width <= 8; width += 2) {
        std::cout << std::format("[{:>{}}]\n", 42, width);
    }
    std::cout << std::format("{:.{}f}\n", 2.718281828, 3);
}
Output
[  42]
[    42]
[      42]
2.718

Formatting your own types

Specialise std::formatter to make your type usable in std::format. The simplest way is to build on an existing formatter:

C++
#include <format>
#include <iostream>

struct Point {
    double x, y;
};

template <>
struct std::formatter<Point> : std::formatter<std::string> {
    auto format(const Point& p, std::format_context& ctx) const {
        return std::formatter<std::string>::format(std::format("({}, {})", p.x, p.y), ctx);
    }
};

int main() {
    Point p{3, 4.5};
    std::cout << std::format("point {} | padded [{:>14}]\n", p, p);
}
Output
point (3, 4.5) | padded [      (3, 4.5)]

format_to and other helpers

std::format_to writes into an existing buffer or output iterator instead of creating a new string, and std::formatted_size tells you how long the result will be. In C++23, std::print and std::println format straight to the console (lesson 50).

C++
#include <format>
#include <iostream>
#include <iterator>
#include <string>

int main() {
    std::string log;
    for (int i = 1; i <= 3; ++i) {
        std::format_to(std::back_inserter(log), "step {} of 3; ", i);
    }
    std::cout << log << '\n';
}
Output
step 1 of 3; step 2 of 3; step 3 of 3;

Try it yourself

Print a temperature conversion table from −10 to 40 °C in steps of 10, with aligned columns and one decimal place.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Output
 Celsius |    Fahr.
     -10 |     14.0
       0 |     32.0
      10 |     50.0
      20 |     68.0
      30 |     86.0
      40 |    104.0