This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3718. P2418R2 broke the overload resolution for `std::basic_format_arg`

Section: 22.14.8.1 [format.arg] Status: New Submitter: Jiang An Opened: 2022-06-17 Last modified: 2022-10-19

Priority: 2

View other active issues in [format.arg].

View all other issues in [format.arg].

View all issues with New status.

Discussion:

While correcting some bugs in MSVC STL, it is found that P2418R2 broke the overload resolution involving non-const lvalue: constructing `basic_format_arg` from a non-`const basic_string` lvalue incorrectly selects the `T&&` overload and uses `handle`, while the separated `basic_string` overload should be selected (i.e. the old behavior before P2418R2 did the right thing). Currently MSVC STL is using a workaround that treats `basic_string` to be as if passed by value during overload resolution.

I think the `T&&` overload should not interfere the old result of overload resolution, which means that when a type is `const`-formattable, the newly added non-`const` mechanism shouldn't be considered.

[2022-07-06; Reflector poll]

Set priority to 2 after reflector poll.

[2022-10-19; Would be resolved by 3631]

Proposed resolution:

This wording is relative to N4910.

> [Drafting note: The below presented wording adds back the `const T&` constructor and its specification that were present before P2418R2. Furthermore it adds an additional constraint to the `T&&` constructor and simplifies the Effects of this constructors to the `handle` construction case.]

1. Modify 22.14.8.1 [format.arg] as indicated:

   > >     namespace std {
   > >       template<class Context>
   > >       class basic_format_arg {
   > >       public:
   > >         class handle;

   > >       private:
   > >         […]
   > >         template<class T> explicit basic_format_arg(T&& v) noexcept; // exposition only
   > >         template<class T> explicit basic_format_arg(const T& v) noexcept; // exposition only
   > >         […]
   > >       };
   > >     }

   > […]

   >     template<class T> explicit basic_format_arg(T&& v) noexcept;

   > > -4- Constraints: The template specialization

   > > >     typename Context::template formatter_type<remove_cvref_t<T>>

   > > meets the BasicFormatter requirements (22.14.6.1 [formatter.requirements]). The extent to which an implementation determines that the specialization meets the BasicFormatter requirements is unspecified, except that as a minimum the expression

   > > >     typename Context::template formatter_type<remove_cvref_t<T>>()
   > > >       .format(declval<T&>(), declval<Context&>())

   > > shall be well-formed when treated as an unevaluated operand (7.2.3 [expr.context]), and if this overload were not declared, the overload resolution would find no usable candidate or be ambiguous. [Note ?: This overload has no effect if the overload resolution among other overloads succeeds. — end note].

   > > -5- Effects:

   > > 1. (5.1) — if `T` is `bool` or `char_type`, initializes `value` with `v`;

   > > 2. (5.2) — otherwise, if `T` is `char` and `char_type` is `wchar_t`, initializes `value` with `static_cast<wchar_t>(v)`;

   > > 3. (5.3) — otherwise, if `T` is a signed integer type (6.8.2 [basic.fundamental]) and `sizeof(T) <= sizeof(int)`, initializes `value` with `static_cast<int>(v)`;

   > > 4. (5.4) — otherwise, if `T` is an unsigned integer type and `sizeof(T) <= sizeof(unsigned int)`, initializes `value` with `static_cast<unsigned int>(v)`;

   > > 5. (5.5) — otherwise, if `T` is a signed integer type and `sizeof(T) <= sizeof(long long int)`, initializes `value` with `static_cast<long long int>(v)`;

   > > 6. (5.6) — otherwise, if `T` is an unsigned integer type and `sizeof(T) <= sizeof(unsigned long long int)`, initializes `value` with `static_cast<unsigned long long int>(v)`;

   > > 7. (5.7) — otherwise, i Initializes `value` with `handle(v)`.

   >     template<class T> explicit basic_format_arg(const T& v) noexcept;

   > > -?- Constraints: The template specialization

   > > >     typename Context::template formatter_type<T>

   > > meets the Formatter requirements (22.14.6.1 [formatter.requirements]). The extent to which an implementation determines that the specialization meets the Formatter requirements is unspecified, except that as a minimum the expression

   > > >     typename Context::template formatter_type<T>()
   > > >       .format(declval<const T&>(), declval<Context&>())

   > > shall be well-formed when treated as an unevaluated operand (7.2.3 [expr.context]).

   > > -?- Effects:

   > > 1. (?.1) — if `T` is `bool` or `char_type`, initializes `value` with `v`;

   > > 2. (?.2) — otherwise, if `T` is `char` and `char_type` is `wchar_t`, initializes `value` with `static_cast<wchar_t>(v)`;

   > > 3. (?.3) — otherwise, if `T` is a signed integer type (6.8.2 [basic.fundamental]) and `sizeof(T) <= sizeof(int)`, initializes `value` with `static_cast<int>(v)`;

   > > 4. (?.4) — otherwise, if `T` is an unsigned integer type and `sizeof(T) <= sizeof(unsigned int)`, initializes `value` with `static_cast<unsigned int>(v)`;

   > > 5. (?.5) — otherwise, if `T` is a signed integer type and `sizeof(T) <= sizeof(long long int)`, initializes `value` with `static_cast<long long int>(v)`;

   > > 6. (?.6) — otherwise, if `T` is an unsigned integer type and `sizeof(T) <= sizeof(unsigned long long int)`, initializes `value` with `static_cast<unsigned long long int>(v)`;

   > > 7. (?.7) — otherwise, initializes `value` with `handle(v)`.