diff --git a/include/fmt/format.h b/include/fmt/format.h index b13bc4cf..8deb365b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3750,6 +3750,46 @@ FMT_CONSTEXPR void handle_dynamic_spec( } #if FMT_USE_NONTYPE_TEMPLATE_ARGS +/** + * `fmt::detail::static_named_arg<...>` is the type of expressions like + * `"argname"_a = value` when `FMT_USE_NONTYPE_TEMPLATE_ARGS` is defined. + * The argument name `"argname"` is in the `fixed_string Str` template argument. + * See [`udl_arg::operator=`](#detail::udl_arg) below. + * + * `T` is the type of the `value` on the right-hand side of `=`, as deduced by + * perfect forwarding. More specifically: + * + * | Value category of `value` | Example | `T` | `const T&` | + * |-----------------------------|-------------------------------------------|--------------|--------------| + * | const lvalue | `const int x; f("arg"_a = x);` | `const int&` | `const int&` | + * | mutable lvalue | `int x; f("arg"_a = x);` | `int&` | `int&` | + * | const xvalue | `const int x; f("arg"_a = std::move(x));` | `const int` | `const int&` | + * | mutable xvalue | `int x; f("arg"_a = std::move(x));` | `int` | `const int&` | + * | const prvalue (class/array) | `f("arg"_a = static_cast(S{}));` | `const S` | `const S&` | + * | const prvalue (other) | `f("arg"_a = static_cast(1));` | `int` | `const int&` | + * | mutable prvalue | `f("arg"_a = 1);` | `int` | `const int&` | + * + * Explanation of the const prvalue case: https://timsong-cpp.github.io/cppwp/std23/expr#expr.type-2. + * The `{fmt}` library doesn't need perfect forwarding, but it is possible to perfectly forward + * the value using `const_cast(value)`. In other words, if you write: + * + * template Str> + * void f(fmt::detail::static_named_arg arg) { + * g(const_cast(arg.value)); + * } + * + * and call it as: + * + * f("arg"_a = any_expression); + * + * then the value category of `any_expression` is perfectly forwarded to `g`. + * + * Using this outside function arguments risks lifetime issues. For example: + * + * auto x = "arg"_a = 1; + * + * After the statement is executed, the prvalue `1` is destroyed, and `x.value` is dangling. + */ template Str> struct static_named_arg : view { @@ -3768,6 +3808,10 @@ template > : std::true_type { }; +/** + * `fmt::detail::udl_arg<...>` is the type of expressions like `"argname"_a`. + * See also [`operator""_a`](#operator""_a). + */ template Str> struct udl_arg { template auto operator=(T&& value) const {