Using an ostream_iterator over pairs

S

ShaunJ

I'm trying to use an ostream_iterator to iterate over a set of pairs.
I've used an ostream_iterator over other types and had no problem.
However, with the following code snippet, I'm getting a compiler
error, and I'm stymied as to why. It's seems to be related to the
string type, because iterating over pairs of other types has worked
fine for me. The (rather verbose) error is:

/usr/include/c++/4.2/bits/stream_iterator.h:196: error: no match for
'operator<<' in '*((std::eek:stream_iterator<std::pair<unsigned int,
, char, std::char_traits<char> >*)this)-
std::eek:stream_iterator<std::pair<unsigned int, std::basic_string<char,
std::char_traits<char>, std::allocator<char> > >, char,
std::char_traits<char> >::_M_stream << __value'

Thanks,
Shaun

#include <iostream>
#include <iterator>
#include <set>
#include <string>

using namespace std;

ostream& operator <<(ostream& o, const pair<unsigned, string>& age)
{
return o << age.second << ": " << age.first;
}

int main()
{
string a("Alice");
string b("Bob");
string c("Carol");
pair<unsigned, string> pa(19, a);
pair<unsigned, string> pb(17, c);
pair<unsigned, string> pc(23, b);
set<pair<unsigned, string> > ages;
ages.insert(pa);
ages.insert(pb);
ages.insert(pc);
ostream_iterator<pair<unsigned, string> > oi(cout, ", ");
copy(ages.begin(), ages.end(), oi);
cout << '\n';
return 0;
}
 
V

Victor Bazarov

ShaunJ said:
I'm trying to use an ostream_iterator to iterate over a set of pairs.
I've used an ostream_iterator over other types and had no problem.
However, with the following code snippet, I'm getting a compiler
error, and I'm stymied as to why. It's seems to be related to the
string type, because iterating over pairs of other types has worked
fine for me. The (rather verbose) error is:

/usr/include/c++/4.2/bits/stream_iterator.h:196: error: no match for
'operator<<' in '*((std::eek:stream_iterator<std::pair<unsigned int,

std::char_traits<char>, std::allocator<char> > >, char,
std::char_traits<char> >::_M_stream << __value'

Thanks,
Shaun

#include <iostream>
#include <iterator>
#include <set>
#include <string>

using namespace std;

ostream& operator <<(ostream& o, const pair<unsigned, string>& age)
{
return o << age.second << ": " << age.first;
}

Since both ostream class and pair template are in 'std' namespace,
you may need to place your operator<< in 'std' namespace. It is
a kludge, IIRC, but it's allowed, unless I'm mistaken.

Try

namespace std {
ostream& operator << ... // etc.
} // std

and see if it helps.
int main()
{
string a("Alice");
string b("Bob");
string c("Carol");
pair<unsigned, string> pa(19, a);
pair<unsigned, string> pb(17, c);
pair<unsigned, string> pc(23, b);
set<pair<unsigned, string> > ages;
ages.insert(pa);
ages.insert(pb);
ages.insert(pc);
ostream_iterator<pair<unsigned, string> > oi(cout, ", ");
copy(ages.begin(), ages.end(), oi);
cout << '\n';
return 0;
}

V
 
S

ShaunJ

Since both ostream class and pair template are in 'std' namespace,
you may need to place your operator<< in 'std' namespace. It is
a kludge, IIRC, but it's allowed, unless I'm mistaken.

Try

namespace std {
ostream& operator << ... // etc.
} // std

and see if it helps.

That worked! Brilliant! Oddly enough, it's only a problem when I have
a pair of std::string, which is in the std namespace. A pair of basic
types (like int) or of a class declared in the global namespace works
without having to put the operator << in the std namespace. Very odd.
Thanks for the tip. Is this hack the accepted (and acceptable?)
solution to this problem?

Cheers,
Shaun
 
J

James Kanze

I'm trying to use an ostream_iterator to iterate over a set of pairs.
I've used an ostream_iterator over other types and had no problem.
However, with the following code snippet, I'm getting a compiler
error, and I'm stymied as to why. It's seems to be related to the
string type, because iterating over pairs of other types has worked
fine for me. The (rather verbose) error is:
/usr/include/c++/4.2/bits/stream_iterator.h:196: error: no match for
'operator<<' in '*((std::eek:stream_iterator<std::pair<unsigned int,
std::char_traits<char>, std::allocator<char> > >, char,
std::char_traits<char> >::_M_stream << __value'
#include <iostream>
#include <iterator>
#include <set>
#include <string>
using namespace std;

I'm dubious about the following operator. It's in the global
namespace, but none of its parameters depend on the global
namespace, so it won't be found during dependent name lookup.
ostream& operator <<(ostream& o, const pair<unsigned, string>& age)
{
return o << age.second << ": " << age.first;
}
int main()
{
string a("Alice");
string b("Bob");
string c("Carol");
pair<unsigned, string> pa(19, a);
pair<unsigned, string> pb(17, c);
pair<unsigned, string> pc(23, b);
set<pair<unsigned, string> > ages;
ages.insert(pa);
ages.insert(pb);
ages.insert(pc);

And dependent name lookup is what will be used here, in the
function std::copy, in the instantiation of ostream_iterator.
ostream_iterator<pair<unsigned, string> > oi(cout, ", ");
copy(ages.begin(), ages.end(), oi);
cout << '\n';
return 0;
}

The usual solution is to define some specific type to be output.
Something like:

class Whatever
{
public:
// ...
friend std::eek:stream&operator<<(
std::eek:stream& dest, Whatever const& obj ) ;
private:
unsigned u ;
std::string n ;
} ;

If you really want to use pair (which is rarely a good idea),
then it's pretty simple to create a wrapper class for it:

class WrappedPair
{
public:
WrappedPair( std::pair< unsigned, std::string > const& obj )
: myObj( &obj )
{
}
friend std::eek:stream&operator<<(
std::eek:stream& dest,
WrappedPair const&
obj )
{
return dest said:
myObj.first ;
return dest ;
}

private:
std::pair< unsigned, std::string > const*
myObj ;
} ;

Then:
std::copy( ages.begin(), ages.end(),
std::eek:stream_iterator< WrappedPair >( std::cout, ",
" ) ) ;

But seriously, a user defined class (with e.g. members age and
name, rather than first and second) is probably more what you
want to begin with.
 
J

James Kanze

ShaunJ wrote:

[...]
Since both ostream class and pair template are in 'std' namespace,
you may need to place your operator<< in 'std' namespace. It is
a kludge, IIRC, but it's allowed, unless I'm mistaken.
Try

namespace std {
ostream& operator << ... // etc.
} // std
and see if it helps.

It's undefined behavior. It might work---it probably will work
on a lot of implementations, but it's undefined behavior all the
same, and could fail.

The real question, of course, is why he's using std::pair to
begin with. And of course, it's also usual to wrap such low
level stuff with a user defined class for formatting, even in
the rare cases where it is appropriate.
 

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

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top