Changing an overloaded operator's signature

An operator can be declared only for the syntax defined for it in the C++ grammar. For example, one can’t define a unary % or a ternary +.

Consider the output operator for a class A. It has the following signature as a non-member:

 
ostream& operator<<(ostream&, A&);


This signature can't be changed. operator<< is a binary operator. Ie, it can take only 2 arguments.

The same is the case with the corresponding >> operator.

In certain situations, this can be restrictive, since it doesn't give the user of this operator, the required flexibility.

For example, consider a class Money used to store a monetary amount and its output operator:

 
ostream& operator<<(ostream&, Money&);


Since a monetary value is involved, we need to display the currency symbol also, which could be either the local or international symbol. If the user should be able to specify this, we would need the above operator to have another parameter, say bool intl.

The operator's signature would then be:

 
ostream& operator<<(ostream&, bool intl, Money&);


Of course, this isn't possible, since the signature is fixed.

How can we proceed in such a situation?

Thanks.
Last edited on
One option is to use use a normal function print_money(Money&, string& currencySymbol).

Another option is to let the money class aquire and store the currency symbol so the operator<< can use it..
How can we proceed in such a situation?
Make a struct that contains the required data:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct intl_Money
{
  bool intl;
  Money money;
  intl_Money(bool i, const Money m) : intl{i}, money{m}
  {
  }
};

ostream& operator<<(ostream&, intl_Money&);

...

cout << intl_Money{true, myMoney};
The I/O Streams model for changing the specifics of output is to use I/O state manipulators. You can write your own manipulator that will inform the subsequent operator<< for Money that it should use full currency symbol.

There is a small example of a custom I/O manipulator at http://en.cppreference.com/w/cpp/io/ios_base/iword
Last edited on
In addition to Cubbi’s answer:

Formatting output is specifically the purpose of locales. C++ has a lot of hooks for messing with formatting stuff. The moneypunct facet is specifically designed for currency.

There is an example1 of using it at http://en.cppreference.com/w/cpp/locale/money_put#Example

There is also a good read about customizing facets here:
http://benjaminwolsey.de/node/78


In short, you should be imbuing your stream with the current locale and using that. If you want a custom locale facet, then write one. (It would be very easy to write one that selectively replaces elements of another facet, such as the currency symbol.)


1As usual, the example at cppreference.com assumes Linux locale specifications.
  The actual locales available to you depend on your system.
  If you would like, I can post something that will deliver a usable locale
  whenever possible from the standard *nix locale string on Windows.

Last edited on
> Since a monetary value is involved, we need to display the currency symbol also, which could
> be either the local or international symbol. If the user should be able to specify this, we would
> need the above operator to have another parameter, say bool intl.

That is the raison d'être for the standard manipulators std::put_money and std::get_money.
They save us from having to write user-defined manipulators for our custom monetary types
(all we need to do is provide for an implicit conversion to either long double or std::string).
http://en.cppreference.com/w/cpp/io/manip/put_money
http://en.cppreference.com/w/cpp/io/manip/get_money

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <string>
#include <locale>
#include <iomanip>
#include <boost/multiprecision/cpp_int.hpp>

struct money
{
    explicit money( unsigned long int_part = 0, unsigned int decimals = 0 )
        : scaled_value( int_part * 100ULL + decimals ) {}

    operator long double() const { return (long double)scaled_value / 100.0L ; } // 2 digits after decimal

    money& operator += ( const money& m ) { scaled_value += m.scaled_value ;  return *this ; }
    money& operator -= ( const money& m ) { scaled_value -= m.scaled_value ;  return *this ; }
    // etc

    enum format : long { SYMBOL, INTL, NONE };

    private:

        boost::multiprecision::cpp_int scaled_value ;
        static const int index ;

    friend money operator+ ( money a, money b ) { return a += b ; }
    friend money operator- ( money a, money b ) { return a -= b ; }
    friend std::ostream& operator<< ( std::ostream& stm, format fmt )
    {
        stm.iword(index) = fmt ;
        return stm ;
    }

    friend std::ostream& operator<< ( std::ostream& stm, const money& m )
    {
        const auto fmt = stm.iword(index) ;
        const auto flgs = stm.flags() ;
        if( fmt == money::NONE ) stm << std::fixed << std::setprecision(2) << (long double)m ;
        else stm << std::showbase << std::put_money( m * 100.0L , fmt == money::INTL ) ;
        stm.flags(flgs) ;
        return stm ;
    }

    // TO DO: input from std::istream
};

const int money::index = std::ios_base::xalloc() ;

int main()
{
    std::cout.imbue( std::locale("en_US.UTF-8") );
    
    // custom facility using xalloc/iword
    const money amt( 1234, 56 ) ; // 1234 dollars, 56 cents
    std::cout << money::SYMBOL << amt << '\n' // $1,234.56
              << money::INTL << amt << '\n' // USD  1,234.56
              << money::NONE << amt << "\n\n" ; // 1,234.56
    
    // using the standard manipulator          
    const money amt2( amt*100 ) ; // interpreted by the library as 123456 cents
    std::cout << std::showbase << std::put_money(amt2) << '\n' // $1,234.56
              << std::put_money( amt2, true ) << '\n' // USD  1,234.56
              << std::noshowbase << std::put_money(amt2) << '\n' ; // 1,234.56
}

http://coliru.stacked-crooked.com/a/cbcc3f51a93eaefc

That is the raison d'être for the standard manipulators std::put_money and std::get_money.
They save us from having to write user-defined manipulators for our custom monetary types

My question was academic. Certainly, in production code, manipulators are a better choice, wherever possible.


The I/O Streams model for changing the specifics of output is to use I/O state manipulators.

Definitely worth exploring, along with the links provided by Duthomas.

Thanks for the answers.
Registered users can post here. Sign in or register to post.