put_money(99)
is unnecessarily undefinedSection: 31.7.8 [ext.manip] Status: New Submitter: Jonathan Wakely Opened: 2017-06-22 Last modified: 2017-06-26
Priority: 3
View all other issues in [ext.manip].
View all issues with New status.
Discussion:
[ext.manip] p5 says:
Requires: The type
moneyT
shall be eitherlong double
or a specialization of thebasic_string
template (Clause 24).
This means that put_money(99)
, put_money(99.)
, put_money("99")
, and
put_money(string_view{"99"})
are all undefined, when in practice they will compile fine and
do the right thing, converting the argument to long double
or std::string
as needed.
long double
or a std::basic_string
, but that will unnecessarily
break code that works fine today. We should accept types convertible to long double
or the relevant
money_put
facet's string_type
(which is not known until we attempt to write the unspecified
type to an ostream).
The requirement is also insufficient, because cout << put_money(wstring(L"99"))
won't compile on any
implementation, despite the argument type being a specialization of basic_string
. This same problem exists
for std::get_money
.
[2017-06-24, Daniel comments and provides wording]
The wording changes below are supposed to support all moneyT
types that are convertible to either
long double
or to money_put/get<Ch, o/istreambuf_iterator<Ch, Tr>>::string_type
(but not to both), where Ch
and Tr
are determined by the concrete instantiated specialization of the
exposition-only function template f
that is used to specify the semantics of put_money
and
get_money
, respectively. XOR-ing the requirements outlaws types that are convertible to both, which
would cause an ambiguity unless we would provide wording that would introduce an ordered application of these
convertibility constraints. This is the rationale for the seemingly odd new Remarks formulation. Note also,
that the wording provided below intentionally attempts to distinguish between the statically testable
conditions based on the is_convertible_v
expressions within the Remarks: element and the well-defined
runtime behaviour requirement of the actually provided argument of deduced type moneyT
within the
pre-existing Requires: element. Another point worth pointing out is that the wording attempts to fix an
currently existing ambiguity of the meaning of the type moneyT
(and to a lesser extend for charT
and
traits
) as either the template parameter of put/get_money
or that of the corresponding template
argument of the exposition-only f
templates. The revised form makes it clearer that it refers to the latter.
put_money(99)
,
put_money(99.)
, and put_money("99")
, but not yet for put_money(string_view{"99"})
,
because string_view
is not convertible to string
. To realize support for the latter, this wording
approach could be extended by referring to is_constructible
instead of is_convertible
, though.
Proposed resolution:
This wording is relative to N4659.
Edit 31.7.8 [ext.manip] as indicated:
template <class moneyT> unspecified get_money(moneyT& mon, bool intl = false);-?- For an expression
-2- Requires:in >> get_money(mon, intl)
described below, letMo
,Ch
, andTr
be the deduced template argument types of the template parametersmoneyT
,charT
, andtraits
, respectively, of the instantiated specialization of the templatef
.The typemoneyT
shall be eitherlong double
or a specialization of thebasic_string
template (Clause 27 [strings])Mo
shall be either convertible tolong double
or shall be convertible tomoney_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type
. -?- Remarks: Ifis_convertible_v<Mo, long double> == is_convertible_v<Mo, money_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type>
, the program is ill-formed. -3- Effects: The expressionin >> get_money(mon, intl)
described below behaves as a formatted input function (31.7.5.3.1 [istream.formatted.reqmts]). -4- Returns: An object of unspecified type such that ifin
is an object of typebasic_istream<
then the expressioncharTCh,traitsTr>in >> get_money(mon, intl)
behaves as if it calledf(in, mon, intl)
, where the functionf
is defined as:template <class charT, class traits, class moneyT> void f(basic_ios<charT, traits>& str, moneyT& mon, bool intl) { using Iter = istreambuf_iterator<charT, traits>; using MoneyGet = money_get<charT, Iter>; ios_base::iostate err = ios_base::goodbit; const MoneyGet& mg = use_facet<MoneyGet>(str.getloc()); mg.get(Iter(str.rdbuf()), Iter(), intl, str, err, mon); if (ios_base::goodbit != err) str.setstate(err); }The expression
in >> get_money(mon, intl)
shall have typebasic_istream<
and valuecharTCh,traitsTr>&in
.template <class moneyT> unspecified put_money(const moneyT& mon, bool intl = false);-?- For an expression
-5- Requires:out << put_money(mon, intl)
described below, letMo
,Ch
, andTr
be the deduced template argument types of the template parametersmoneyT
,charT
, andtraits
, respectively, of the instantiated specialization of the templatef
.The typemoneyT
shall be eitherlong double
or a specialization of thebasic_string
template (Clause 27 [strings])Mo
shall be either convertible tolong double
or shall be convertible tomoney_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type
. -?- Remarks: Ifis_convertible_v<Mo, long double> == is_convertible_v<Mo, money_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type>
, the program is ill-formed. -6- Returns: An object of unspecified type such that ifout
is an object of typebasic_ostream<
then the expressioncharTCh,traitsTr>out << put_money(mon, intl)
behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) that callsf(out, mon, intl)
, where the functionf
is defined as:template <class charT, class traits, class moneyT> void f(basic_ios<charT, traits>& str, const moneyT& mon, bool intl) { using Iter = ostreambuf_iterator<charT, traits>; using MoneyPut = money_put<charT, Iter>; const MoneyPut& mp = use_facet<MoneyPut>(str.getloc()); const Iter end = mp.put(Iter(str.rdbuf()), intl, str, str.fill(), mon); if (end.failed()) str.setstate(ios::badbit); }The expression
out << put_money(mon, intl)
shall have typebasic_ostream<
and valuecharTCh,traitsTr>&out
.