Positional and named arguments
Positional and named arguments exist for the same reason: the order of the holes in a format string shouldn't be dictated by the order your variables happen to be declared in â especially once translators get involved.
Positionalâ
{N} picks argument N (zero-indexed), and the same argument can be reused as many times as you
like.
fmt::format("{1} {0} {1}", "world", "hello"); // "hello world hello"
Namedâ
fmt::arg("name", value) attaches a name to an argument, and {name} in the format string refers to
it. The _a user-defined literal from <fmt/args.h> is shorthand for fmt::arg.
#include <fmt/args.h>
using namespace fmt::literals;
fmt::format("{name} is {age} years old", "name"_a = "Ada", "age"_a = 36);
// equivalent, without the literal:
fmt::format("{name} is {age} years old", fmt::arg("name", "Ada"), fmt::arg("age", 36));
Why this matters for translationâ
A string handed to a translator can reorder its fields freely as long as the call site passes named arguments â the C++ code doesn't need to change to match a language where the sentence structure differs.
// English: "{name} scored {score} points"
// German: "{score} Punkte fĂźr {name}" â same fmt::arg calls, reordered template
With positional or automatic indexing, reordering the sentence in translation means reordering (and re-testing) the argument list in code. With named arguments, only the translated string changes.
Dynamic argument listsâ
When the set of arguments isn't known until runtime â building a log line from a variable number of
key/value pairs, say â fmt::dynamic_format_arg_store accumulates arguments one at a time before a
single fmt::vformat call.
fmt::dynamic_format_arg_store<fmt::format_context> store;
for (const auto& [key, value] : fields) {
store.push_back(fmt::arg(key.c_str(), value));
}
std::string line = fmt::vformat(format_string, store);
Lifetimeâ
fmt::arg("name", value) stores a reference to value, not a copy. Building a dynamic_format_arg_store
across a loop and only formatting after values have gone out of scope is a dangling-reference bug.
See Common pitfalls for the exact shape of
this mistake and the fix.
std::format compatibilityâ
If code needs to compile against both fmt and std::format, named arguments are one of the features
that doesn't port. See Relationship to std::format
for the full list of what each side has that the other doesn't.
See alsoâ
- Format strings and arguments â the basics these examples build on.
- The format function family â where the formatted result goes once the arguments are resolved.
- Relationship to std::format â what does and doesn't carry over to the standard facility.
- fmt overview â the full doc set.