Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Modern C++20/23

std::format (C++20)

Modern String Formatting

C++20’s std::format provides Python-style string formatting that’s type-safe, efficient, and more intuitive than iostream or printf.

Basic Syntax

#include <format>
#include <string>

std::string name = "Alice";
int age = 30;

// C++20 format
std::string msg = std::format("Hello, {}! You are {} years old.", name, age);
// "Hello, Alice! You are 30 years old."

Comparison with Alternatives

// printf: not type-safe, no std::string support
printf("Hello, %s! You are %d years old.\n", name.c_str(), age);

// iostream: verbose, awkward
std::ostringstream oss;
oss << "Hello, " << name << "! You are " << age << " years old.";
std::string msg = oss.str();

// std::format: clean and type-safe
auto msg = std::format("Hello, {}! You are {} years old.", name, age);

Positional Arguments

Reference arguments by position:

std::format("{0} {1} {0}", "Hello", "World");  // "Hello World Hello"
std::format("{1} {0}", "World", "Hello");      // "Hello World"

Format Specifications

Integers

int num = 42;

std::format("{}", num);         // "42"
std::format("{:d}", num);       // "42" (decimal)
std::format("{:x}", num);       // "2a" (hexadecimal lowercase)
std::format("{:X}", num);       // "2A" (hexadecimal uppercase)
std::format("{:o}", num);       // "52" (octal)
std::format("{:b}", num);       // "101010" (binary)

std::format("{:06d}", num);     // "000042" (zero-padded, width 6)
std::format("{:+d}", num);      // "+42" (show sign)
std::format("{: d}", num);      // " 42" (space for positive)

Floating-Point

double pi = 3.14159265359;

std::format("{}", pi);          // "3.14159265359"
std::format("{:.2f}", pi);      // "3.14" (2 decimal places)
std::format("{:.5f}", pi);      // "3.14159"
std::format("{:e}", pi);        // "3.141593e+00" (scientific)
std::format("{:E}", pi);        // "3.141593E+00"
std::format("{:g}", pi);        // "3.14159" (general format)

std::format("{:10.2f}", pi);    // "      3.14" (width 10)
std::format("{:010.2f}", pi);   // "0000003.14" (zero-padded)

Strings

std::string text = "Hello";

std::format("{}", text);        // "Hello"
std::format("{:10}", text);     // "Hello     " (width 10, left-aligned)
std::format("{:>10}", text);    // "     Hello" (right-aligned)
std::format("{:^10}", text);    // "  Hello   " (centered)
std::format("{:*<10}", text);   // "Hello*****" (fill with *)
std::format("{:.3}", text);     // "Hel" (truncate to 3)

Alignment and Fill

int n = 42;

std::format("{:<5}", n);        // "42   " (left-aligned, width 5)
std::format("{:>5}", n);        // "   42" (right-aligned)
std::format("{:^5}", n);        // " 42  " (centered)
std::format("{:*>5}", n);       // "***42" (fill with *, right-aligned)
std::format("{:0>5}", n);       // "00042" (zero-padded)

Type-Specific Formatting

Pointers

int* ptr = &value;
std::format("{}", ptr);         // "0x7ffc12345678"
std::format("{:p}", ptr);       // "0x7ffc12345678"

Booleans

bool flag = true;
std::format("{}", flag);        // "true"
std::format("{:s}", flag);      // "true" (string representation)
std::format("{:d}", flag);      // "1" (numeric representation)

Characters

char ch = 'A';
std::format("{}", ch);          // "A"
std::format("{:d}", ch);        // "65" (ASCII value)

Compile-Time Format String Checking

Format strings are checked at compile-time:

int n = 42;

std::format("{}", n);           // OK
std::format("{:d}", n);         // OK
std::format("{:f}", n);         // Compile error: can't format int as float
std::format("{} {}", n);        // Compile error: too few arguments

Custom Type Formatting

Extend formatting for your types:

struct Point {
    int x, y;
};

template<>
struct std::formatter<Point> {
    constexpr auto parse(format_parse_context& ctx) {
        return ctx.begin();
    }
    
    auto format(const Point& p, format_context& ctx) const {
        return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

Point p{10, 20};
std::format("Point: {}", p);    // "Point: (10, 20)"

Advanced Custom Formatting

Support format specifications:

template<>
struct std::formatter<Point> {
    char presentation = 'c';  // 'c' for cartesian, 'p' for polar
    
    constexpr auto parse(format_parse_context& ctx) {
        auto it = ctx.begin();
        if (it != ctx.end() && (*it == 'c' || *it == 'p')) {
            presentation = *it++;
        }
        return it;
    }
    
    auto format(const Point& p, format_context& ctx) const {
        if (presentation == 'p') {
            double r = std::sqrt(p.x*p.x + p.y*p.y);
            double theta = std::atan2(p.y, p.x);
            return std::format_to(ctx.out(), "r={:.2f}, θ={:.2f}", r, theta);
        }
        return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
    }
};

Point p{3, 4};
std::format("{:c}", p);  // "(3, 4)"
std::format("{:p}", p);  // "r=5.00, θ=0.93"

format_to: Output Iterators

Write directly to output iterators for efficiency:

std::string buffer;
std::format_to(std::back_inserter(buffer), "Value: {}", 42);
// buffer == "Value: 42"

std::vector<char> vec;
std::format_to(std::back_inserter(vec), "Hello {}", "World");

format_to_n: Bounded Output

Format with size limit:

char buffer[20];
auto result = std::format_to_n(buffer, sizeof(buffer), "Value: {}", 12345);
// result.out points past the last written character
// result.size is the total that would have been written

formatted_size: Size Calculation

Calculate size without formatting:

size_t size = std::formatted_size("Value: {}, {}", 42, "test");
std::string buffer;
buffer.reserve(size);
std::format_to(std::back_inserter(buffer), "Value: {}, {}", 42, "test");

Localization Support

std::locale loc("de_DE.UTF-8");
double value = 1234.56;

std::format("{:L}", value);              // "1,234.56" (default locale)
std::format(loc, "{:L}", value);         // "1.234,56" (German locale)

Escaping Braces

std::format("{{}}");           // "{}"
std::format("{{{}}} ", 42);    // "{42} "

vformat: Runtime Format Strings

When format string isn’t known at compile-time:

std::string fmt = get_format_from_user();  // Runtime string
std::string result = std::vformat(fmt, std::make_format_args(arg1, arg2));

Performance Benefits

std::format is typically faster than iostream:

// Iostream: multiple virtual calls, manipulator overhead
std::ostringstream oss;
oss << "Value: " << std::setw(10) << std::setfill('0') << value;

// std::format: single operation, optimized
auto str = std::format("Value: {:010}", value);

Migration from printf

// printf
printf("%s: %d items, %.2f total\n", name.c_str(), count, total);

// std::format
std::println("{}: {} items, {:.2f} total", name, count, total);
// or
std::cout << std::format("{}: {} items, {:.2f} total\n", name, count, total);

C++23 std::print and std::println

C++23 adds convenience functions:

std::print("Hello, {}!\n", name);           // Print to stdout
std::println("Value: {}", value);           // Print with newline
std::print(stderr, "Error: {}\n", msg);     // Print to stderr
Conceptual Check

What is the advantage of std::format over printf?

Interactive Lab

Complete the Code

double price = 19.99;
int quantity = 3;

// Format as: "Total: $59.97 (3 items)"
std::string msg = std::format();
Runtime Environment

Interactive Lab

1#include <iostream>
2#include <format>
3#include <string>
4 
5int main() {
6 std::string name = "Alice";
7 int score = 95;
8 double average = 87.456;
9
10 std::cout << std::format(&quot;Student: {}
11&quot;, name);
12 std::cout << std::format(&quot;Score: {:3d}/100
13&quot;, score);
14 std::cout << std::format(&quot;Average: {:6.2f}
15&quot;, average);
16 std::cout << std::format(&quot;Hex: 0x{:04X}
17&quot;, score);
18 std::cout << std::format(&quot;Binary: 0b{:08b}
19&quot;, score);
20 std::cout << std::format(&quot;{:=^30}
21", " Summary &quot;);
22
23 return 0;
24}
System Console

Waiting for signal...