# How to Use Positional and Named Arguments with fmtlib: A Complete Guide

> Master fmtlib's positional and named arguments. Learn to use {} and {name} with fmt::arg for flexible, readable string formatting in your C++ projects.

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

---

**Use `{}` or `{0}` for positional arguments and `fmt::arg("name", value)` for named arguments, then reference them as `{name}` in your format string.**

fmtlib provides flexible, type-safe formatting through two argument referencing styles. The library's implementation in `fmtlib/fmt` allows you to mix positional indices, automatic sequencing, and named parameters within a single format string. This article breaks down the source-level mechanics and practical usage patterns.

## Understanding Positional Arguments

Positional arguments in fmtlib work through explicit indexing or automatic ordering. The library resolves these at compile time for maximum performance.

### Explicit and Implicit Positioning

You have two syntax options for positional arguments:

- **`{N}`** — explicit zero-based index (e.g., `{0}`, `{1}`)
- **`{}`** — implicit next argument in order

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

int main() {
    // Implicit order: arguments consumed left-to-right
    std::string s1 = fmt::format("First: {}, Second: {}", 10, 20);
    // Result: "First: 10, Second: 20"

    // Explicit indices: reuse arguments or reorder them
    std::string s2 = fmt::format("First: {0}, Second: {1}", 10, 20);
    std::string s3 = fmt::format("Second: {1}, First: {0}", 10, 20);
    // Result: "Second: 20, First: 10"
}

```

In [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h), the parser extracts arguments directly from the parameter pack using the specified index. The `none` index constant triggers sequential consumption for `{}` placeholders.

## Using Named Arguments with fmtlib

Named arguments improve code readability and allow flexible parameter ordering. The implementation carries runtime name metadata for lookup.

### Creating Named Arguments with `fmt::arg`

The helper function `fmt::arg` (defined around line 2799 in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)) constructs a `named_arg<T>` wrapper:

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

int main() {
    std::string s = fmt::format(
        "Name: {name}, Age: {age}",
        fmt::arg("name", "Alice"),
        fmt::arg("age", 30)
    );
    // Result: "Name: Alice, Age: 30"
}

```

### The `named_arg` Implementation

The underlying structure in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) binds a name string to a value reference:

```cpp
template <typename T, typename Char = char>
struct named_arg : view {
    const Char* name;          // argument name for runtime lookup
    const T&    value;         // reference to actual value

    named_arg(const Char* n, const T& v) : name(n), value(v) {}

    static_assert(!is_named_arg<T>::value, "nested named arguments");
};

```

The `named_arg_store` built by `make_format_args` creates a lookup table of `named_arg_info` entries mapping names to indices.

## Mixing Positional and Named Arguments

fmtlib allows hybrid format strings combining both styles. The internal argument store maintains separate counters for each type.

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

int main() {
    // Positional {0} mixed with named {age}
    std::string s = fmt::format(
        "{0} is {age} years old",
        "Bob",                          // positional argument 0
        fmt::arg("age", 45)             // named argument
    );
    // Result: "Bob is 45 years old"

    // Multiple positionals with named parameters interspersed
    std::string report = fmt::format(
        "{0}: {1} scored {points} points in quarter {quarter}",
        "Game Update",                  // {0}
        "Lakers",                       // {1}
        fmt::arg("points", 87),
        fmt::arg("quarter", 3)
    );
}

```

The parser in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h) (around line 2629) performs runtime name lookup by iterating `named_arg_info` entries when encountering a named placeholder.

## Wide-Character Support

Named arguments work with `wchar_t` strings through overloads in [`include/fmt/xchar.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/xchar.h):

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

int main() {
    std::wstring ws = fmt::format(
        L"Coordinates: ({x}, {y})",
        fmt::arg(L"x", 3.5),
        fmt::arg(L"y", 7.2)
    );
    // Result: L"Coordinates: (3.5, 7.2)"
}

```

Use the `L` prefix consistently for both format strings and argument names.

## Error Handling and Validation

fmtlib provides compile-time safety for common errors:

- **Duplicate names** — detected at compile time via `report_error("duplicate named arg")` in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h)
- **Missing arguments** — compile-time error when a referenced index or name lacks a corresponding value
- **Type mismatches** — caught by the formatter's type-erasure mechanism

The [`compile.h`](https://github.com/fmtlib/fmt/blob/main/compile.h) header provides additional static checks when names must be known at compile time.

## Summary

- **Positional arguments** use `{N}` for explicit indices or `{}` for automatic sequencing
- **Named arguments** require `fmt::arg("name", value)` and are referenced as `{name}`
- **Mixing styles** is fully supported with separate internal tracking
- **Implementation files**: [`core.h`](https://github.com/fmtlib/fmt/blob/main/core.h) (parsing, `named_arg`), [`format.h`](https://github.com/fmtlib/fmt/blob/main/format.h) (public API, `fmt::arg`), [`xchar.h`](https://github.com/fmtlib/fmt/blob/main/xchar.h) (wide character support)
- **Performance**: positional arguments resolve at compile time; named arguments use lightweight runtime lookup

## Frequently Asked Questions

### What is the performance cost of named arguments versus positional arguments?

Named arguments incur a small runtime overhead for name-to-index lookup via the `named_arg_info` table. Positional arguments resolve entirely at compile time. For hot paths, prefer positional syntax; for readability in message formatting, named arguments are optimal.

### Can I reuse the same named argument multiple times in a format string?

Yes. Once defined with `fmt::arg("name", value)`, you can reference `{name}` any number of times in the format string. The lookup occurs per placeholder, retrieving the same cached index each time.

### Does fmtlib detect duplicate named argument definitions?

Yes. Defining `fmt::arg("x", a)` and `fmt::arg("x", b)` in the same call triggers a compile-time error through `report_error("duplicate named arg")` in the `init_named_arg` logic within [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h).

### How does fmtlib handle named arguments with custom types?

Custom types work transparently when they provide a `formatter<T>` specialization. The `named_arg` template stores a `const T&`, so your formatter receives the value directly without extra wrapping. The type-checking occurs at the same point as positional arguments.