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.

### 2984. `put_money(99)` is unnecessarily undefined

Section: 29.7.7 [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 either `long double` or a specialization of the `basic_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.

We could change it to be "otherwise the program is ill-formed", or to remove the function templates from overload resolution when the argument is not `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.

It should be emphasized that this extension of the current wording would provide support for `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.

1. Edit 29.7.7 [ext.manip] as indicated:

   >     template <class moneyT> unspecified get_money(moneyT& mon, bool intl = false);

   > > -?- For an expression `in >> get_money(mon, intl)` described below, let `Mo`, `Ch`, and `Tr` be the deduced template argument types of the template parameters `moneyT`, `charT`, and `traits`, respectively, of the instantiated specialization of the template `f`.

   > > -2- Requires: The type `moneyT` shall be either `long double` or a specialization of the `basic_string` template (Clause 21 [strings])`Mo` shall be either convertible to `long double` or shall be convertible to `money_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type`.

   > > -?- Remarks: If `is_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 expression `in >> get_money(mon, intl)` described below behaves as a formatted input function (29.7.4.3.1 [istream.formatted.reqmts]).

   > > -4- Returns: An object of unspecified type such that if `in` is an object of type `basic_istream<charTCh, traitsTr>` then the expression `in >> get_money(mon, intl)` behaves as if it called `f(in, mon, intl)`, where the function `f` 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 type `basic_istream<charTCh, traitsTr>&` and value `in`.

   >     template <class moneyT> unspecified put_money(const moneyT& mon, bool intl = false);

   > > -?- For an expression `out << put_money(mon, intl)` described below, let `Mo`, `Ch`, and `Tr` be the deduced template argument types of the template parameters `moneyT`, `charT`, and `traits`, respectively, of the instantiated specialization of the template `f`.

   > > -5- Requires: The type `moneyT` shall be either `long double` or a specialization of the `basic_string` template (Clause 21 [strings])`Mo` shall be either convertible to `long double` or shall be convertible to `money_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type`.

   > > -?- Remarks: If `is_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 if `out` is an object of type `basic_ostream<charTCh, traitsTr>` then the expression `out << put_money(mon, intl)` behaves as a formatted output function (29.7.5.3.1 [ostream.formatted.reqmts]) that calls `f(out, mon, intl)`, where the function `f` 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 type `basic_ostream<charTCh, traitsTr>&` and value `out`.