std::basic_format_arg
Section: 28.5.8.1 [format.arg] Status: Resolved Submitter: Jiang An Opened: 2022-06-17 Last modified: 2023-03-23
Priority: 2
View all other issues in [format.arg].
View all issues with Resolved 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.
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]
Previous resolution [SUPERSEDED]:
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 theT&&
constructor and simplifies the Effects of this constructors to thehandle
construction case.]
Modify 28.5.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 (28.5.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:
(5.1) — ifT
isbool
orchar_type
, initializesvalue
withv
;
(5.2) — otherwise, ifT
ischar
andchar_type
iswchar_t
, initializesvalue
withstatic_cast<wchar_t>(v)
;
(5.3) — otherwise, ifT
is a signed integer type (6.8.2 [basic.fundamental]) andsizeof(T) <= sizeof(int)
, initializesvalue
withstatic_cast<int>(v)
;
(5.4) — otherwise, ifT
is an unsigned integer type andsizeof(T) <= sizeof(unsigned int)
, initializesvalue
withstatic_cast<unsigned int>(v)
;
(5.5) — otherwise, ifT
is a signed integer type andsizeof(T) <= sizeof(long long int)
, initializesvalue
withstatic_cast<long long int>(v)
;
(5.6) — otherwise, ifT
is an unsigned integer type andsizeof(T) <= sizeof(unsigned long long int)
, initializesvalue
withstatic_cast<unsigned long long int>(v)
;
(5.7) — otherwise, iInitializesvalue
withhandle(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 (28.5.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) — if
T
isbool
orchar_type
, initializesvalue
withv
;(?.2) — otherwise, if
T
ischar
andchar_type
iswchar_t
, initializesvalue
withstatic_cast<wchar_t>(v)
;(?.3) — otherwise, if
T
is a signed integer type (6.8.2 [basic.fundamental]) andsizeof(T) <= sizeof(int)
, initializesvalue
withstatic_cast<int>(v)
;(?.4) — otherwise, if
T
is an unsigned integer type andsizeof(T) <= sizeof(unsigned int)
, initializesvalue
withstatic_cast<unsigned int>(v)
;(?.5) — otherwise, if
T
is a signed integer type andsizeof(T) <= sizeof(long long int)
, initializesvalue
withstatic_cast<long long int>(v)
;(?.6) — otherwise, if
T
is an unsigned integer type andsizeof(T) <= sizeof(unsigned long long int)
, initializesvalue
withstatic_cast<unsigned long long int>(v)
;(?.7) — otherwise, initializes
value
withhandle(v)
.
[2023-03-22 Resolved by the adoption of 3631 in Issaquah. Status changed: New → Resolved.]
Proposed resolution: