# How to Use `fmt::format_arg_store` in C++: Dynamic vs Static Argument Storage

> Learn to use fmt::format_arg_store in C++ for dynamic and static argument storage. Discover runtime building and compile-time generation for low-level formatting.

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

---

**`fmt::format_arg_store` provides two storage mechanisms—`dynamic_format_arg_store` for runtime argument building and the compile-time `format_arg_store` generated by `make_format_args`—that both convert to `basic_format_args` for low-level formatting functions.**

The `{fmt}` library (fmtlib/fmt) offers flexible utilities for constructing argument lists that can be passed to formatting functions like `fmt::vformat` and `fmt::format_to`. Understanding how to use `fmt::format_arg_store` effectively allows you to build formatting pipelines that minimize allocations and support dynamic, runtime-configurable formatting scenarios.

## Understanding the Two Storage Types

The library provides distinct storage strategies depending on whether your argument list is known at compile time or built dynamically at runtime.

### Dynamic Storage: `dynamic_format_arg_store`

The **`dynamic_format_arg_store<Context>`** class, defined in [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h), provides a runtime-modifiable store where arguments can be added incrementally using `push_back`. It automatically manages copies, references, and named arguments, making it ideal for scenarios where the number and types of arguments are determined during program execution.

### Static Storage: `format_arg_store`

The **`format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>`** template, declared in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), represents a compile-time-fixed argument store. Most users never instantiate this type manually; instead, the library generates it automatically when you call `fmt::make_format_args`. This approach eliminates dynamic allocation entirely by encoding argument counts and types in template parameters.

Both storage types expose an implicit conversion operator to **`fmt::basic_format_args<Context>`**, which is the concrete type accepted by the library's low-level formatting APIs.

## Runtime Argument Building with `dynamic_format_arg_store`

For scenarios requiring runtime argument construction, use `dynamic_format_arg_store` to build argument lists incrementally.

### Basic Usage and `push_back`

The `push_back` method is overloaded to handle values, references, and named arguments. Arguments that require dynamic allocation (such as `std::string` or user-defined types) are automatically stored in an internal `detail::dynamic_arg_list` linked structure, while built-in types and string views are stored by reference when possible.

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

int main() {
    // Create a store for the default char-based format context
    fmt::dynamic_format_arg_store<fmt::format_context> store;

    // Push positional arguments by value
    store.push_back(42);
    store.push_back("hello");

    // Push a reference to allow external mutation
    std::string mutable_str = "world";
    store.push_back(std::cref(mutable_str));

    // Push a named argument
    store.push_back(fmt::arg("greeting", "Welcome"));

    // Format using the store
    std::string out = fmt::vformat("{} {} {} {greeting}", store);
    // Result: "42 hello world Welcome"
}

```

### Handling Named Arguments and References

Named arguments can reference external variables using `std::reference_wrapper`, allowing the stored value to reflect changes made after insertion. The template `need_copy<T>` (lines 78-89 in [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h)) determines whether an argument is stored by copy or reference based on its type characteristics.

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

int main() {
    fmt::dynamic_format_arg_store<fmt::format_context> store;
    int count = 5;
    
    // Store named argument by reference
    store.push_back(fmt::arg("cnt", std::cref(count)));
    
    // Modify original after insertion
    count = 10;
    
    std::string s = fmt::vformat("{cnt}", store);
    // Result: "10" (reflects updated value)
}

```

## Compile-Time Storage with `make_format_args`

When argument types and counts are known at compile time, use **`fmt::make_format_args`** to generate a lightweight, allocation-free storage object. This function returns a `format_args` object that internally uses the static `format_arg_store` specialization.

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

void format_with_args() {
    // Compiler generates static format_arg_store internally
    auto args = fmt::make_format_args(1, 2.5, "three");
    
    // Pass directly to low-level formatting function
    std::string result = fmt::vformat("{} {} {}", args);
    // Result: "1 2.5 three"
}

```

This approach is preferred for hot paths where dynamic allocation overhead must be avoided, as the compiler can lay out storage layout without heap operations.

## Internal Implementation Details

Understanding the storage mechanics helps optimize usage for performance-critical applications.

### Storage Layout

In [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h), `dynamic_format_arg_store` maintains two primary containers:
- **`data_`** (vector of `basic_format_arg<Context>`): Holds compact representations of each argument
- **`named_info_`**: Stores metadata mapping argument names to indices (lines 16-27)

When named arguments are added, a placeholder is inserted at the front of `data_` and the name-to-index mapping is updated accordingly.

### Dynamic Allocation Strategy

Types that cannot fit directly within `basic_format_arg` are stored in a **`detail::dynamic_arg_list`**. The `push` method creates a `typed_node<T>` that owns a copy of the argument, linked via `std::unique_ptr` (`head_`). This ensures type-safe storage for complex objects while maintaining reference semantics for lightweight types.

### Copy vs Reference Logic

The `need_copy<T>` trait (lines 78-89) implements the following rules:
- Types wrapped in `std::reference_wrapper`, `string_view`, or built-in types are stored by reference
- All other types (including `std::string` and user-defined types) are copied into the dynamic list

The conversion operator (lines 33-36) constructs a `basic_format_args` view over `data_`, passing the size and a boolean flag indicating the presence of named arguments.

## Mixing Positional and Named Arguments

`dynamic_format_arg_store` supports hybrid formatting strings that combine positional indices and named placeholders.

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

int main() {
    fmt::dynamic_format_arg_store<fmt::format_context> store;
    
    store.push_back(100);                           // Positional argument 0
    store.push_back(fmt::arg("city", "Paris"));     // Named argument
    
    // Mixing positional and named references
    std::string txt = fmt::vformat("{0} is in {city}", store);
    // Result: "100 is in Paris"
}

```

The store maintains separate tracking for positional and named arguments, allowing flexible format string composition without reordering the underlying data structure.

## Summary

- **`dynamic_format_arg_store`** in [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h) provides runtime argument building via `push_back`, supporting both values and references.
- **`make_format_args`** generates compile-time static storage (`format_arg_store`) for allocation-free formatting when argument types are known upfront.
- Both storage types convert implicitly to `fmt::basic_format_args<Context>` for use with `fmt::vformat` and related functions.
- Named arguments use `fmt::arg()` and can reference external variables via `std::cref` for dynamic value updates.
- Internal `need_copy<T>` logic optimizes storage by avoiding copies for reference wrappers and view types.
- Key implementation files are [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h) for dynamic storage and [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) for the static variant and `make_format_args`.

## Frequently Asked Questions

### What is the difference between `fmt::format_arg_store` and `fmt::dynamic_format_arg_store`?

`fmt::format_arg_store` is a template class used for compile-time fixed argument lists, automatically instantiated when you call `fmt::make_format_args`. It encodes argument counts in template parameters and avoids dynamic allocation. `fmt::dynamic_format_arg_store` is designed for runtime construction, allowing you to add arguments incrementally using `push_back` and supporting runtime-determined argument counts.

### How do I pass arguments by reference to avoid copies?

Wrap your variables in `std::reference_wrapper` using `std::cref` or `std::ref` when calling `push_back`. The internal `need_copy<T>` template (defined in [`include/fmt/args.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/args.h)) detects reference wrappers and stores them by reference rather than copying them into the `dynamic_arg_list`. This allows the formatted output to reflect changes to the original variables made after insertion.

### Can I mix positional and named arguments in the same format string?

Yes. When using `dynamic_format_arg_store`, you can push positional arguments with `push_back(value)` and named arguments with `push_back(fmt::arg("name", value))`. The format string can then reference both styles simultaneously (e.g., `"{0} and {name}"`). The store automatically manages the separate indexing systems for positional and named arguments.

### When should I use `make_format_args` instead of `dynamic_format_arg_store`?

Use `make_format_args` when the number and types of arguments are known at compile time and you need maximum performance. This approach generates a static `format_arg_store` with zero heap allocation. Use `dynamic_format_arg_store` when building argument lists conditionally at runtime, when argument types vary based on program state, or when you need to reuse the same argument set across multiple format calls.