This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
3631.basic_format_arg(T&&)
should use remove_cvref_t<T>
throughout
Section: 28.5.8.1 [format.arg] Status: C++23 Submitter: Tim Song Opened: 2021-11-03 Last modified: 2023-11-22
Priority: 3
View all other issues in [format.arg].
View all issues with C++23 status.
Discussion:
P2418R2 changed basic_format_arg
's constructor to take a forwarding reference but didn't change 28.5.8.1 [format.arg]/5 which inspects various properties of T
. Now that the deduced type can be cvref-qualified, they need to be removed before the checks.
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
Two suggestions to just change it to be T&
because we don't need forwarding references here, and only accepting lvalues prevents forming dangling references.
Previous resolution [SUPERSEDED]:
This wording is relative to N4901.
Modify 28.5.8.1 [format.arg] as indicated:
template<class T> explicit basic_format_arg(T&& v) noexcept;-?- Let
-4- Constraints: The template specializationTD
beremove_cvref_t<T>
.typename Context::template formatter_type<remove_cvref_t<T>TD>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>TD>() .format(declval<T&>(), declval<Context&>())shall be well-formed when treated as an unevaluated operand (7.2.3 [expr.context]).
-5- Effects:
(5.1) — if
T D
isbool
orchar_type
, initializesvalue
withv
;(5.2) — otherwise, if
T D
ischar
andchar_type
iswchar_t
, initializesvalue
withstatic_cast<wchar_t>(v)
;(5.3) — otherwise, if
T D
is a signed integer type (6.9.2 [basic.fundamental]) andsizeof(T D) <= sizeof(int)
, initializesvalue
withstatic_cast<int>(v)
;(5.4) — otherwise, if
T D
is an unsigned integer type andsizeof(T D) <= sizeof(unsigned int)
, initializesvalue
withstatic_cast<unsigned int>(v)
;(5.5) — otherwise, if
T D
is a signed integer type andsizeof(T D) <= sizeof(long long int)
, initializesvalue
withstatic_cast<long long int>(v)
;(5.6) — otherwise, if
T D
is an unsigned integer type andsizeof(T D) <= sizeof(unsigned long long int)
, initializesvalue
withstatic_cast<unsigned long long int>(v)
;(5.7) — otherwise, initializes
value
withhandle(v)
.
[2022-10-19; Jonathan provides improved wording]
During reflector prioritization it was pointed out that forwarding references are unnecessary (arguments are always lvalues), and so using T&
would be simpler.
In order to resolve the overload ambiguities discussed in 3718(i) replace all unary constructor overloads with a single constructor that works for any formattable type.
Previous resolution [SUPERSEDED]:
This wording is relative to N4917.
Modify 28.5.8.1 [format.arg] as indicated:
template<class T> explicit basic_format_arg(T&& v) noexcept;-4- Constraints:
T
satisfiesformattable<char_type>
. The template specializationtypename 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]).
-?- Preconditions: If
decay_t<T>
ischar_type*
orconst char_type*
,static_cast<const char_type*>(v)
points to a NTCTS (3.36 [defns.ntcts]).-5- Effects: Let
TD
beremove_const_t<T>
.
(5.1) — if
T D
isbool
orchar_type
, initializesvalue
withv
;(5.2) — otherwise, if
T D
ischar
andchar_type
iswchar_t
, initializesvalue
withstatic_cast<wchar_t>(v)
;(5.3) — otherwise, if
T D
is a signed integer type (6.9.2 [basic.fundamental]) andsizeof(T D) <= sizeof(int)
, initializesvalue
withstatic_cast<int>(v)
;(5.4) — otherwise, if
T D
is an unsigned integer type andsizeof(T D) <= sizeof(unsigned int)
, initializesvalue
withstatic_cast<unsigned int>(v)
;(5.5) — otherwise, if
T D
is a signed integer type andsizeof(T D) <= sizeof(long long int)
, initializesvalue
withstatic_cast<long long int>(v)
;(5.6) — otherwise, if
T D
is an unsigned integer type andsizeof(T D) <= sizeof(unsigned long long int)
, initializesvalue
withstatic_cast<unsigned long long int>(v)
;(5.?) — otherwise, if
TD
is a standard floating-point type, initializesvalue
withv
;(5.?) — otherwise, if
TD
is a specialization ofbasic_string_view
orbasic_string
andTD::value_type
ischar_type
, initializesvalue
withbasic_string_view<char_type>(v.data(), v.size())
;(5.?) — otherwise, if
decay_t<TD>
ischar_type*
orconst char_type*
, initializesvalue
withstatic_cast<const char_type*>(v)
;(5.?) — otherwise, if
is_void_v<remove_pointer_t<TD>>
istrue
oris_null_pointer_v<TD>
istrue
, initializesvalue
withstatic_cast<const void*>(v)
;(5.7) — otherwise, initializes
value
withhandle(v)
.-?- [Note: Constructing
basic_format_arg
from a pointer to a member is ill-formed unless the user provides an enabled specialization offormatter
for that pointer to member type. — end note]explicit basic_format_arg(float n) noexcept; explicit basic_format_arg(double n) noexcept; explicit basic_format_arg(long double n) noexcept;-6- Effects: Initializes
value
withn
.explicit basic_format_arg(const char_type* s) noexcept;-7- Preconditions:
s
points to a NTCTS (3.36 [defns.ntcts]).-8- Effects: Initializes
value
withs
.template<class traits>> explicit basic_format_arg(basic_string_view<char_type, traits> s) noexcept;-9- Effects: Initializes
value
withbasic_string_view<char_type>(s.data(), s.size())
.template<class traits, class Allocator>> explicit basic_format_arg( const basic_string<char_type, traits, Allocator>& s) noexcept;-10- Effects: Initializes
value
withbasic_string_view<char_type>(s.data(), s.size())
.explicit basic_format_arg(nullptr_t) noexcept;-11- Effects: Initializes
value
withstatic_cast<const void*>(nullptr)
.template<class T> explicit basic_format_arg(T* p) noexcept;-12- Constraints:
is_void_v<T>
istrue
.-13- Effects: Initializes
value
withp
.-14- [Note: Constructing
basic_format_arg
from a pointer to a member is ill-formed unless the user provides an enabled specialization offormatter
for that pointer to member type. — end note]Modify 28.5.8.1 [format.arg] p17 and p18 as indicated:
template<class T> explicit handle(T&& v) noexcept;-17- Let
— (17.1)
TD
beremove_ cvref const_t<T>
,— (17.2)
const-formattable
betrue
iftypename Context::template formatter_type<TD>().format(declval<const TD&>(), declval<Context&>())
is well-formed, otherwisefalse
,— (17.3)
TQ
beconst TD
ifconst-formattable
istrue
const TD
satisfiesformattable<char_type>
andTD
otherwise.-18- Mandates:
const-formattable
isformattable<const TD, char_type>
|| !is_const_v< remove_reference_t<T >true
.-19- Effects: Initializes
ptr_
withaddressof(val)
andformat_
with[](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) { typename Context::template formatter_type<TD> f; parse_ctx.advance_to(f.parse(parse_ctx)); format_ctx.advance_to(f.format(*const_cast<TQ*>(static_cast<const TD*>(ptr)), format_ctx)); }
[2022-11-10; Jonathan provides improved wording]
[2022-11-29; Casey expands the issue to also cover make_format_args
]
[2023-01-11; Jonathan adds the missing edits to the class synopsis]
[Issaquah 2023-02-07; LWG]
Edited proposed resolution to remove extra =
in concept definition and capitialize start of (5.1). Move to Immediate for C++23
[2023-02-13 Approved at February 2023 meeting in Issaquah. Status changed: Immediate → WP.]
Proposed resolution:
This wording is relative to N4917.
Modify 28.5.6.3 [format.formattable] as indicated:
-1- Let fmt-iter-for<charT>
be an unspecified type that models output_iterator<const charT&>
(24.3.4.10 [iterator.concept.output]).
template<class T, class Context, class Formatter = typename Context::template formatter_type<remove_const_t<T>>> concept formattable-with = // exposition only semiregular<Formatter> && requires (Formatter& f, const Formatter& cf, T&& t, Context fc, basic_format_parse_context<typename Context::char_type> pc) { { f.parse(pc) } -> same_as<typename decltype(pc)::iterator>; { cf.format(t, fc) } -> same_as<typename Context::iterator>; }; template<class T, class charT> concept formattable = semiregular<formatter<remove_cvref_t<T>, charT>> && requires(formatter<remove_cvref_t<T>, charT> f, const formatter<remove_cvref_t<T>, charT> cf, T t, basic_format_context<fmt-iter-for<charT>, charT> fc, basic_format_parse_context<charT> pc) { { f.parse(pc) } -> same_as<basic_format_parse_context<charT>::iterator>; { cf.format(t, fc) } -> same_as<fmt-iter-for<charT>>; }; formattable-with<remove_reference_t<T>, basic_format_context<fmt-iter-for<charT>>>;
Modify 28.5.8.1 [format.arg] as indicated:
namespace std { template<class Context> class basic_format_arg { public: class handle; private: using char_type = typename Context::char_type; // exposition only variant<monostate, bool, char_type, int, unsigned int, long long int, unsigned long long int, float, double, long double, const char_type*, basic_string_view<char_type>, const void*, handle> value; // exposition only template<class T> explicit basic_format_arg(T&& v) noexcept; // exposition only explicit basic_format_arg(float n) noexcept; // exposition only explicit basic_format_arg(double n) noexcept; // exposition only explicit basic_format_arg(long double n) noexcept; // exposition only explicit basic_format_arg(const char_type* s); // exposition only template<class traits> explicit basic_format_arg( basic_string_view<char_type, traits> s) noexcept; // exposition only template<class traits, class Allocator> explicit basic_format_arg( const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only explicit basic_format_arg(nullptr_t) noexcept; // exposition only template<class T> explicit basic_format_arg(T* p) noexcept; // exposition only…
template<class T> explicit basic_format_arg(T&& v) noexcept;-4- Constraints:
T
satisfiesformattable-with<Context>
. The template specializationtypename 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]).
-?- Preconditions: If
decay_t<T>
ischar_type*
orconst char_type*
,static_cast<const char_type*>(v)
points to a NTCTS (3.36 [defns.ntcts]).-5- Effects: Let
TD
beremove_const_t<T>
.
(5.1) — If if
T D
isbool
orchar_type
, initializesvalue
withv
;(5.2) — otherwise, if
T D
ischar
andchar_type
iswchar_t
, initializesvalue
withstatic_cast<wchar_t>(v)
;(5.3) — otherwise, if
T D
is a signed integer type (6.9.2 [basic.fundamental]) andsizeof(T D) <= sizeof(int)
, initializesvalue
withstatic_cast<int>(v)
;(5.4) — otherwise, if
T D
is an unsigned integer type andsizeof(T D) <= sizeof(unsigned int)
, initializesvalue
withstatic_cast<unsigned int>(v)
;(5.5) — otherwise, if
T D
is a signed integer type andsizeof(T D) <= sizeof(long long int)
, initializesvalue
withstatic_cast<long long int>(v)
;(5.6) — otherwise, if
T D
is an unsigned integer type andsizeof(T D) <= sizeof(unsigned long long int)
, initializesvalue
withstatic_cast<unsigned long long int>(v)
;(5.?) — otherwise, if
TD
is a standard floating-point type, initializesvalue
withv
;(5.?) — otherwise, if
TD
is a specialization ofbasic_string_view
orbasic_string
andTD::value_type
ischar_type
, initializesvalue
withbasic_string_view<char_type>(v.data(), v.size())
;(5.?) — otherwise, if
decay_t<TD>
ischar_type*
orconst char_type*
, initializesvalue
withstatic_cast<const char_type*>(v)
;(5.?) — otherwise, if
is_void_v<remove_pointer_t<TD>>
istrue
oris_null_pointer_v<TD>
istrue
, initializesvalue
withstatic_cast<const void*>(v)
;(5.7) — otherwise, initializes
value
withhandle(v)
.-?- [Note: Constructing
basic_format_arg
from a pointer to a member is ill-formed unless the user provides an enabled specialization offormatter
for that pointer to member type. — end note]explicit basic_format_arg(float n) noexcept; explicit basic_format_arg(double n) noexcept; explicit basic_format_arg(long double n) noexcept;-6- Effects: Initializes
value
withn
.explicit basic_format_arg(const char_type* s) noexcept;-7- Preconditions:
s
points to a NTCTS (3.36 [defns.ntcts]).-8- Effects: Initializes
value
withs
.template<class traits>> explicit basic_format_arg(basic_string_view<char_type, traits> s) noexcept;-9- Effects: Initializes
value
withbasic_string_view<char_type>(s.data(), s.size())
.template<class traits, class Allocator>> explicit basic_format_arg( const basic_string<char_type, traits, Allocator>& s) noexcept;-10- Effects: Initializes
value
withbasic_string_view<char_type>(s.data(), s.size())
.explicit basic_format_arg(nullptr_t) noexcept;-11- Effects: Initializes
value
withstatic_cast<const void*>(nullptr)
.template<class T> explicit basic_format_arg(T* p) noexcept;-12- Constraints:
is_void_v<T>
istrue
.-13- Effects: Initializes
value
withp
.-14- [Note: Constructing
basic_format_arg
from a pointer to a member is ill-formed unless the user provides an enabled specialization offormatter
for that pointer to member type. — end note]
Modify 28.5.8.1 [format.arg] p17 and p18 as indicated:
template<class T> explicit handle(T&& v) noexcept;-17- Let
— (17.1)
TD
beremove_ cvref const_t<T>
,— (17.2)
const-formattable
betrue
iftypename Context::template formatter_type<TD>().format(declval<const TD&>(), declval<Context&>())
is well-formed, otherwisefalse
,— (17.3)
TQ
beconst TD
ifconst-formattable
istrue
const TD
satisfiesformattable-with<Context>
andTD
otherwise.-18- Mandates:
const-formattable || !is_const_v<remove_reference_t<T>>
istrue
.TQ
satisfiesformattable-with<Context>
.-19- Effects: Initializes
ptr_
withaddressof(val)
andformat_
with[](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) { typename Context::template formatter_type<TD> f; parse_ctx.advance_to(f.parse(parse_ctx)); format_ctx.advance_to(f.format(*const_cast<TQ*>(static_cast<const TD*>(ptr)), format_ctx)); }
Modify 28.5.8.2 [format.arg.store] p2 as indicated:
template<class Context = format_context, class... Args> format-arg-store<Context, Args...> make_format_args(Args&&... fmt_args);-2- Preconditions: The type
typename Context::template formatter_type< remove_cvref_t<T
i>>
meets the BasicFormatter requirements (28.5.6.1 [formatter.requirements]) for eachT
i inArgs
.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4