# How to Use `fmt::make_format_args` in C++: A Complete Guide to Building Format Argument Stores

> Learn how to use fmt::make_format_args to build type-safe argument stores for zero-copy formatting in C++ with this complete guide.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-12

---

**`fmt::make_format_args` builds a compile-time type-safe argument store that holds references to formatting arguments, enabling zero-copy formatting across the {fmt} library.**

The {fmt} library provides `fmt::make_format_args` as the foundational mechanism for capturing formatting arguments before they are rendered. Defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), this function creates a `detail::format_arg_store` object that powers every high-level formatting API in the codebase, from `fmt::format` to `fmt::printf`.

## What Is `fmt::make_format_args`?

`fmt::make_format_args` is a variadic template function that constructs a lightweight container holding references to your formatting arguments. According to the source code in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) (lines 71-78), the function signature is:

```cpp
template <typename Context = context, typename... T,
          int NUM_ARGS      = int(sizeof...(T)),
          int NUM_NAMED_ARGS= detail::count_named_args<T...>(),
          ullong DESC      = detail::make_descriptor<Context, T...>()>
constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
    -> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
  return {{args...}};
}

```

The template parameters capture compile-time metadata:

- **`Context`** – The formatting context type, defaulting to `fmt::context`.
- **`NUM_ARGS`** – Compile-time count of positional arguments derived from `sizeof...(T)`.
- **`NUM_NAMED_ARGS`** – Count of named arguments created with `fmt::arg`, calculated via `detail::count_named_args`.
- **`DESC`** – A 64-bit compile-time descriptor encoding type information for optimized formatting dispatch.

The function returns a `detail::format_arg_store` that implicitly converts to `fmt::format_args`, allowing it to be passed to any API consuming format arguments.

## How `fmt::make_format_args` Works Internally

The implementation stores references (`T&... args`) rather than copies, which enables zero-allocation formatting paths. This design requires that the original objects outlive the `format_args` object.

The `format_arg_store` structure records type information through the `DESC` template parameter. As implemented in `fmtlib/fmt`, this descriptor allows the library to select optimal formatting routines at compile time without runtime type introspection.

Internally, `fmt::make_format_args` serves as the argument forwarding mechanism for higher-level functions. For example, in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) (lines 573-574), the printf-style API forwards its arguments through `make_format_args`:

```cpp
return fmt::make_format_args<basic_printf_context<Char>>(args...);

```

## Practical Examples

### Storing Arguments for Deferred Formatting

Use `fmt::make_format_args` when you need to capture arguments now but format them later, or when passing arguments through multiple abstraction layers:

```cpp
#include <fmt/core.h>
#include <string>

int main() {
    int value = 42;
    std::string name = "answer";

    // Build the format argument store.
    auto args = fmt::make_format_args(value, name);

    // Pass to any function accepting fmt::format_args.
    fmt::print("{} is the {}\n", args);  // Output: 42 is the answer
}

```

### Using Named Arguments with `fmt::make_format_args`

When formatting strings with named placeholders, combine `fmt::arg` with `make_format_args`:

```cpp
#include <fmt/core.h>

int main() {
    int age = 30;
    
    auto args = fmt::make_format_args(
        fmt::arg("name", "Alice"),
        fmt::arg("age", age)
    );

    fmt::print("{name} is {age} years old\n", args);
    // Output: Alice is 30 years old
}

```

### Building Custom Formatting Functions

Create wrapper functions that pre-build argument stores for logging or internationalization pipelines:

```cpp
#include <fmt/core.h>
#include <fmt/format.h>

template <typename... Ts>
void custom_log(fmt::format_string<Ts...> fmt_str, Ts&&... values) {
    // Construct argument store once.
    auto args = fmt::make_format_args(values...);
    
    // Forward to vprint or custom sinks.
    fmt::vprint(fmt_str.str, args);
}

int main() {
    custom_log("Error {}: {}\n", 404, "Not Found");
}

```

## Integration with {fmt} Library Components

`fmt::make_format_args` serves as the bridge between user code and the library's internal formatting engine:

- **[`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)** – Contains the primary definition and `format_arg_store` implementation.
- **[`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h)** – Uses `make_format_args` to adapt C-style printf calls to the type-safe formatting system.
- **[`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h)** – Leverages the same mechanism for wide-character (`wchar_t`) formatting contexts.
- **[`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)** – High-level functions like `fmt::format` internally utilize `make_format_args` to process variadic template arguments.

## Lifetime and Safety Considerations

Because `fmt::make_format_args` captures references to its arguments, you must ensure the referenced objects remain valid through the formatting operation. The `format_arg_store` does not copy string data or arithmetic values—it merely stores pointers and metadata.

**Safe usage:**
- Arguments passed to `make_format_args` must outlive the resulting `format_args` object.
- Do not store the returned `format_arg_store` beyond the scope of the referenced variables.
- Temporary objects passed directly to `make_format_args` are safe only if formatting occurs immediately in the same expression.

## Summary

- **`fmt::make_format_args`** creates a zero-copy, type-safe argument container defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h).
- It returns a `detail::format_arg_store` that converts to `fmt::format_args` for use with any formatting function.
- The function captures compile-time metadata (`NUM_ARGS`, `NUM_NAMED_ARGS`, `DESC`) to optimize formatting performance.
- It powers internal APIs including `fmt::printf` (in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h)) and wide-character formatting.
- Arguments are stored by reference, requiring careful lifetime management.

## Frequently Asked Questions

### What is the difference between `fmt::make_format_args` and variadic templates?

**`fmt::make_format_args`** converts runtime values into a type-erased yet type-safe container suitable for APIs that cannot expose template parameters in their interface. While variadic templates preserve types at compile time, `make_format_args` bundles those types into a `format_arg_store` that can be passed through virtual functions or stored for later processing, as seen in `fmt::vprint` implementations.

### Does `fmt::make_format_args` copy my strings and objects?

No. The function stores references (`T&...`) to the original arguments. This zero-copy design enables high-performance formatting but requires that you manage object lifetimes carefully. The source code in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) explicitly constructs the store as `{{args...}}`, preserving reference semantics.

### Can I use `fmt::make_format_args` with `fmt::arg` for named parameters?

Yes. `make_format_args` accepts `fmt::arg` objects directly, and the template machinery automatically counts named arguments via `detail::count_named_args`. This allows you to build dynamic format strings with named placeholders while maintaining compile-time type safety.

### When should I use `fmt::make_format_args` instead of `fmt::format` directly?

Use **`fmt::make_format_args`** when you need to separate argument capture from formatting execution. Common scenarios include implementing custom logging frameworks, queuing format operations for asynchronous processing, or creating adapter layers between your code and {fmt}'s internal APIs. For immediate string formatting, `fmt::format` remains the simpler choice.