copy map to ostream_iterator

J

Jeff

std::copy to std::eek:stream_iterator doesn't seem to
work with maps and pairs.

Shouldn't I expect something like this to work?

The G++ error message when USE_COPY_TO_PRINT is
defined as a non-zero number follows.

---------------- error message --------------------

<snip lower 'instantiated from' template chain>

constCorrectness.cpp:99: instantiated from here
/usr/include/c++/3.3.1/bits/stream_iterator.h:141: e
rror: no match for
'operator<<' in '*this->std::eek:stream_iterator<Pair
II, char,
std::char_traits<char> >::_M_stream << __value'
<snip candidate templates list>

-------------------- environment -------------------

Windows 2K, CygWin, g++

--------------------- makefile ---------------------

#!/usr/bin make

printmaps.exe : \
printmaps.o
printmaps.o : \
printmaps.cpp

%.exe : %.o
g++ -g -pedantic -Os -fexceptions -o $@ $^
%.o : %.cpp
g++ -g -pedantic -Os -fexceptions -o $@ -c $<
# EOF

------------------- compiler -----------------------
cmd /c g++ --version
g++ (GCC) 3.3.1 (cygming special)
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying co
nditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FO
R A PARTICULAR PURPOSE.

------------------- source code --------------------
#include <map>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <sstream>

// -------------------------------------------------
// PROBLEM NOTE:
// Everything compiles with USE_COPY_TO_PRINT
// set to 0. Set to 1 to show the problem.
// -------------------------------------------------

#define USE_COPY_TO_PRINT 1

typedef std::pair<int ,int> PairII;

std::string
to_s(PairII const & pii) {
std::stringstream ss;
ss << "(" << pii.first << ","
<< pii.second << ")";
return ss.str();
}

std::eek:stream &
operator << (
std::eek:stream & os,
PairII const & pii) {
os << to_s(pii);
return os;
}

#if !USE_COPY_TO_PRINT

// -------------------------------------------------
// PROBLEM NOTE:
// This is a helper for the work-around.
// -------------------------------------------------

void
print(PairII const & pii) {
std::cout << to_s(pii) << "\n";
}

#endif

int main() {
size_t const N(5);
PairII a[N] = {
PairII(1,2),
PairII(3,4),
PairII(5,6),
PairII(7,8),
PairII(9,10)
};
std::map<int,int> m(
a,
a + N
);
std::cout << a[0] << "\n";
std::cout << *m.begin() << "\n";

#if USE_COPY_TO_PRINT

// -------------------------------------------------
// PROBLEM NOTE:
// This is the part that doesn't work.
// -------------------------------------------------

std::copy(
a,
a + N,
std::eek:stream_iterator<PairII>(
std::cout,
"\n"
)
);
std::copy(
m.begin(),
m.end(),
std::eek:stream_iterator<PairII>(
std::cout,
"\n"
)
);

#else

// -------------------------------------------------
// PROBLEM NOTE:
// This is the work around.
// -------------------------------------------------

std::for_each(
a,
a + N,
print
);
std::for_each(
m.begin(),
m.end(),
print
);

#endif

}
// EOF
 
J

John Harrison

Jeff said:
std::copy to std::eek:stream_iterator doesn't seem to
work with maps and pairs.

Shouldn't I expect something like this to work?

Well perhaps. But there is no insertion operator defined for pairs by the
C++ standard, and surprisingly there is no useful way to define one.

This does not work

template <class T, class U>
ostream& operator<<(ostream& out, std::pair<T, U> const& p)
{
...
}

because the compiler will not look in the global namespace when trying to
find an operator<< for std::eek:stream and std::pair. The only namespace it
will look at is the std namespace, so this might work

namespace std
{
template <class T, class U>
ostream& operator<<(ostream& out, std::pair<T, U> const& p)
{
...
}
}

but it is not legal C++ because it is not legal to add definitions to the
std namespace.

In my view this is a defect in the C++ language.

john
 
J

John Harrison

John Harrison said:
Well perhaps. But there is no insertion operator defined for pairs by the
C++ standard, and surprisingly there is no useful way to define one.

This does not work

template <class T, class U>
ostream& operator<<(ostream& out, std::pair<T, U> const& p)
{
...
}

because the compiler will not look in the global namespace when trying to
find an operator<< for std::eek:stream and std::pair.

I should add that this is only because the compiler is looking for
operator<< from within std::copy which obviously is in the std namespace. If
you wrote your own version of std::copy in the global namespace, then I
believe it would work.

john
 
J

John Harrison

I should add that this is only because the compiler is looking for
operator<< from within std::copy

That of course is complete nonsense. operator << is being accessed from
inside std::eek:stream_iterator, which again is in the std namespace, but a bit
harder to rewrite than std::copy.

john
 
C

CornedBee

Jeff said:
std::copy to std::eek:stream_iterator doesn't seem to
work with maps and pairs.

Shouldn't I expect something like this to work?

<snipalot/>

No, actually not. std::copy is in std, std::eek:stream is in std and
std::pair is in std, thus there's no reason why it should look outside
std for a <<. I don't quite understand why it won't let you define the
operator in std though.

For what it's worth, a better workaround than the one you have would
use transform:

std::transform(themap.begin(), themap.end(),
std::eek:stream_iterator<std::string>(cout, "\n"), to_s);
 
J

Jeff

namespace std
{
template <class T, class U>
ostream& operator<<(ostream& out, std::pair<T, U> const& p)
{
...
}
}

Thanks for your help. That works, but I decided to go with transform so I
don't have to inject into 'std' or rewrite the ostream_iterator outside of
std.
 
J

Jeff

std::transform(themap.begin(), themap.end(),
std::eek:stream_iterator<std::string>(cout, "\n"), to_s);

Thanks. This works well.

The standard library has failed the least-surprises design criteria on this
point, hasn't it? Shouldn't the designers of pair have included stream
support for it? Maybe not, if they'd have to guess how you want to
represent formatted pairs. There may be lots of options like

(1,2)
1=2
1:2

and so on.
 
J

Jerry Coffin

[ ... ]
this might work

namespace std
{
template <class T, class U>
ostream& operator<<(ostream& out, std::pair<T, U> const& p)
{
...
}
}

but it is not legal C++ because it is not legal to add definitions to the
std namespace.

If you look carefully, you'll find that there are circumstances under
which it IS legal to add definitions to the std namespace -- and
unless memory serves me particularly ill tonight, this would fit
within the rules.
 
J

John Harrison

Jerry Coffin said:
[ ... ]
this might work

namespace std
{
template <class T, class U>
ostream& operator<<(ostream& out, std::pair<T, U> const& p)
{
...
}
}

but it is not legal C++ because it is not legal to add definitions to the
std namespace.

If you look carefully, you'll find that there are circumstances under
which it IS legal to add definitions to the std namespace -- and
unless memory serves me particularly ill tonight, this would fit
within the rules.

I think your memory serves you ill. This is covered in 17.4.3.1. The only
exception is for specializations of existing templates. Additional function
overloads (templates or not) are not allowed.

john
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top