overloading << for map and multimap simultaneously

O

ozizus

I overloaded operator << for STL map successfully:

template <typename T1, typename T2> ostream & operator << (ostream &
o, map <T1,T2> & m)
{
//code
}

the code works like a charm. Now, I want the same functionality for
multimap. Since their interface is same for the problem at hand, I
want to make collection name a template parameter like this:

template <typename MAP, typename T1, typename T2> ostream & operator
<< (ostream & o, MAP<T1,T2> & m)
{
//code
}

The compiler errors like this: syntax error : missing ')' before '<'

thx,
ozi.
 
V

Victor Bazarov

ozizus said:
I overloaded operator << for STL map successfully:

template <typename T1, typename T2> ostream & operator << (ostream &
o, map <T1,T2> & m)
{
//code
}

the code works like a charm. Now, I want the same functionality for
multimap. Since their interface is same for the problem at hand, I
want to make collection name a template parameter like this:

template <typename MAP, typename T1, typename T2> ostream & operator
<< (ostream & o, MAP<T1,T2> & m)
{
//code
}

The compiler errors like this: syntax error : missing ')' before '<'

The compiler cannot deduce all three from the argument you give.
Besides, if 'MAP' is a template (as you imply by using the template
syntax when declaring the second argument), then it's not a "typename"
argument, it's a "template <class,class> class MAP" argument. Try

template<template<class, class> class MAP, typename T1, typename T2>
ostream& operator << (...

but it is most likely not going to work. I recommend dropping the T1
and T2 arguments and just do

template<class MAP> ostream& operator <<( ostream& o, MAP const& m)

(you do want to have the output object 'const', by the way).

V
 
K

Kai-Uwe Bux

ozizus said:
I overloaded operator << for STL map successfully:

template <typename T1, typename T2> ostream & operator << (ostream &
o, map <T1,T2> & m)
{
//code
}

the code works like a charm. Now, I want the same functionality for
multimap. Since their interface is same for the problem at hand, I
want to make collection name a template parameter like this:

template <typename MAP, typename T1, typename T2> ostream & operator
<< (ostream & o, MAP<T1,T2> & m)
{
//code
}

The compiler errors like this: syntax error : missing ')' before '<'

a) technically, you want something like:

template < template <class, class > class MAP, typename T1, typename T2 >

Read up on template template parameters (as opposed to typename template
parameters).

b) Using a template template parameter here, is a BadIdea(tm) since your
template would match too many things, e.g., it would match a pair<S,T>. In
that case, your code will not work.

Thus: you should just do another template for multimaps.

c) You may want to make your code aware of the Allocator and Comparison
typename parameters in the map template. I.e.,

template< typename S, typename T, typename A, typename C >
std::eek:stream & operator<< ( std::eek:stream & ostr,
const std::multimap< S, T, A, C > & v );



Best

Kai-Uwe Bux
 
T

terminator

I overloaded operator << for STL map successfully:

template <typename T1, typename T2> ostream & operator << (ostream &
o, map <T1,T2> & m)
{
//code

}

the code works like a charm. Now, I want the same functionality for
multimap. Since their interface is same for the problem at hand, I
want to make collection name a template parameter like this:

template <typename MAP, typename T1, typename T2> ostream & operator
<< (ostream & o, MAP<T1,T2> & m)
{
//code

}

The compiler errors like this: syntax error : missing ')' before '<'

thx,
ozi.

you can simply write:

template<typename MapT>
ostream & operator << (ostream & o, const MapT& m){
typedef typename MapT::Key_type T1;
typedef typename MapT::mapped_type T2;
//code

return o;
};


if your compiler supports concepts ,things will be easier :

auto concept map_like <MapT>{//what a (multi)map looks like.
typedef typename MapT::key_type key_type;
typedef typename MapT::mapped_type mapped_type;
//etc:

};

template<map_like MapT>//argument type must behave like a map
ostream & operator << (ostream & o, const MapT& m){
//code

return o;
};


regards,
FM.
 
O

ozizus

a) technically, you want something like:

template < template <class, class > class MAP, typename T1, typename T2 >

Read up on template template parameters (as opposed to typename template
parameters).

I tried the code below, but got error C2065: 'T2' : undeclared
identifier.

template<template<class,class> class MAP, typename T1, typename t2>
ostream & operator << (ostream & o, MAP<T1,T2> & m)

C++ is huge, is not it, lots of reading up to do.. Thx anyway.
 
O

ozizus

template<typename MapT>
ostream & operator << (ostream & o, const MapT& m){
typedef typename MapT::Key_type T1;
typedef typename MapT::mapped_type T2;
//code

return o;

};

template<typename MapT> ostream & operator << (ostream & o, MapT & m)
{
typedef typename MapT::key_type T1;
typedef typename MapT::mapped_type T2;
...
}

gives the cute one liner below:

error LNK2019: unresolved external symbol "class
std::basic_ostream<char,struct std::char_traits<char> > & __cdecl
operator<<<class std::vector<int,class std::allocator<int> >,class
std::map<class std::vector<int,class std::allocator<int> >,int,struct
std::less<class std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,int> > > >(class
std::basic_ostream<char,struct std::char_traits<char> > &,class
std::map<class std::vector<int,class std::allocator<int> >,class
std::map<class std::vector<int,class std::allocator<int> >,int,struct
std::less<class std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,int> > >,struct std::less<class
std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,class std::map<class
std::vector<int,class std::allocator<int> >,int,struct std::less<class
std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,int> > > > > > &)" (??$?6V?$vector@HV?
$allocator@H@std@@@std@@V?$map@V?$vector@HV?$allocator@H@std@@@std@@HU?
$less@V?$vector@HV?$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$
$CBV?$vector@HV?$allocator@H@std@@@std@@H@std@@@2@@1@@@YAAAV?
$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$map@V?
$vector@HV?$allocator@H@std@@@std@@V?$map@V?$vector@HV?
$allocator@H@std@@@std@@HU?$less@V?$vector@HV?
$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$$CBV?$vector@HV?
$allocator@H@std@@@std@@H@std@@@2@@2@U?$less@V?$vector@HV?
$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$$CBV?$vector@HV?
$allocator@H@std@@@std@@V?$map@V?$vector@HV?$allocator@H@std@@@std@@HU?
$less@V?$vector@HV?$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$
$CBV?$vector@HV?$allocator@H@std@@@std@@H@std@@@2@@2@@std@@@2@@1@@Z)
referenced in function _main


MSVS2005, seems like no concepts. Thx anyway.
 
K

Kai-Uwe Bux

ozizus said:
I tried the code below, but got error C2065: 'T2' : undeclared
identifier.

You have a typo:
template<template<class,class> class MAP, typename T1, typename t2>
^^
ostream & operator << (ostream & o, MAP<T1,T2> & m)
^^


Also: make that a MAP<T1,T2> const & m.
^^^^^

Also: don't do this anyway. I already explained why it is a bad idea.

If you want to avoid code duplication, implement a template

template < typename Cont >
std::eek:stream & write_sequence ( std::eek:stream & os, Cont const & c )

and use it to implement output for map and multimap.

C++ is huge, is not it, lots of reading up to do.. Thx anyway.

True.


Best

Kai-Uwe Bux
 
J

James Kanze

I overloaded operator << for STL map successfully:
template <typename T1, typename T2> ostream & operator << (ostream &
o, map <T1,T2> & m)
{
//code
}
the code works like a charm.

Are you sure? In what namespace did you put it? If you put it
in std, then you have undefined behavior. And if you put it in
some other namespace, if you use it in a template, the compiler
will only find it when it is instantiated on a class in the same
namespace (dependant name look-up oblige).

In general, you can't reliably overload operator<< generically
for any of the container types in the standard library (nor can
you overload it for instantiations where only standard types are
involved). In general, of course, you don't want to either.
 
O

ozizus

Are you sure? In what namespace did you put it?

I put it in no namespace. I use "using namespace std;". This may be
bad practice but it works. Since I overloaded << for other containers,
I can output any object that is a combination of these containers in
one line. Same is true for >> too.

eg.
map<vector<int>, set<string>> m;
//code
cout<<m;

very powerfull.
 
T

terminator

template<typename MapT> ostream & operator << (ostream & o, MapT & m)
{
typedef typename MapT::key_type T1;
typedef typename MapT::mapped_type T2;
..

}

gives the cute one liner below:

error LNK2019: unresolved external symbol "class
std::basic_ostream<char,struct std::char_traits<char> > & __cdecl
operator<<<class std::vector<int,class std::allocator<int> >,class
std::map<class std::vector<int,class std::allocator<int> >,int,struct
std::less<class std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,int> > > >(class
std::basic_ostream<char,struct std::char_traits<char> > &,class
std::map<class std::vector<int,class std::allocator<int> >,class
std::map<class std::vector<int,class std::allocator<int> >,int,struct
std::less<class std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,int> > >,struct std::less<class
std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,class std::map<class
std::vector<int,class std::allocator<int> >,int,struct std::less<class
std::vector<int,class std::allocator<int> > >,class
std::allocator<struct std::pair<class std::vector<int,class
std::allocator<int> > const ,int> > > > > > &)" (??$?6V?$vector@HV?
$allocator@H@std@@@std@@V?$map@V?$vector@HV?$allocator@H@std@@@std@@HU?
$less@V?$vector@HV?$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$
$CBV?$vector@HV?$allocator@H@std@@@std@@H@std@@@2@@1@@@YAAAV?
$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$map@V?
$vector@HV?$allocator@H@std@@@std@@V?$map@V?$vector@HV?
$allocator@H@std@@@std@@HU?$less@V?$vector@HV?
$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$$CBV?$vector@HV?
$allocator@H@std@@@std@@H@std@@@2@@2@U?$less@V?$vector@HV?
$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$$CBV?$vector@HV?
$allocator@H@std@@@std@@V?$map@V?$vector@HV?$allocator@H@std@@@std@@HU?
$less@V?$vector@HV?$allocator@H@std@@@std@@@2@V?$allocator@U?$pair@$
$CBV?$vector@HV?$allocator@H@std@@@std@@H@std@@@2@@2@@std@@@2@@1@@Z)
referenced in function _main
That looks like a multiple definition error.try defining some
function :

ostream& out(mytype& m,ostream& os);
MSVS2005, seems like no concepts. Thx anyway.

no surprize it is too new and for ms2005.

MSVC supports templates as arguments but I did not want to suggest
that.As long as (multi)map takes 4 type arguments(last two have
default params) you will need five arguments for such a template:

#define T typename
template< template <T,T,T,T> T MapT/*0*/,T Key/*1*/, T Value/*2*/,T
Predicate/*3*/,T Alloc/*4*/>
ostream& (ostream& os, MapT<Key,Value,Predicate,Alloc> & m);

on MSVS (multi)map/set derive from 'template<typename traits> struct
_Tree' defined in <xtree> header.you can overload for '_Tree' but that
affects (multi)set as well(potentially more classes):

#include<xtree>
using namespace std;
template <typename traits>
ostream& (ostream& os, _Tree<traits> & m);

regards,
FM.
 
J

James Kanze

I put it in no namespace. I use "using namespace std;". This may be
bad practice but it works.

No it doesn't. (I mean putting your operator in no namespace.)
Try something like:

std::copy(
c.begin(), c.end(),
std::eek:stream_iterator< std::map< X, Y > >( std::cout,
"\n" ) ) ;

Your operator won't be found, unless either X or Y are user
defined types in the same namespace as your operator.
Since I overloaded << for other containers,
I can output any object that is a combination of these containers in
one line. Same is true for >> too.
eg.
map<vector<int>, set<string>> m;
//code
cout<<m;
very powerfull.

Very unmaintainable, you mean. Not something you really want to
do; it's a definite recepe for undefined behavior.

The answer to your question is simple: there's no way to define
an overload of operator<< for a standard type in a way that is
guaranteed to work in all cases, and you don't want to, even if
you could.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,665
Latest member
salkete

Latest Threads

Top