Can you tell me why the program below does not compile? GCC says:
$ make mapout.o
c++ -O2 -c mapout.cpp
/usr/include/g++/bits/stream_iterator.h: In member function `
std::ostream_iterator<_Tp, _CharT, _Traits>& std::ostream_iterator<_Tp,
_CharT, _Traits>::operator=(const _Tp&) [with _Tp = std::pair<const
std::string, std::string>, _CharT = char, _Traits =
std::char_traits<char>]
':
[...]
mapout.cpp:21: instantiated from here
/usr/include/g++/bits/stream_iterator.h:141: error: no match for
'operator<<'
in '*this->std::ostream_iterator<std::pair<const std::string,
std::string>,
char, std::char_traits<char> >::_M_stream << __value'
I understand that ostream_iterator::operator=(rhs) calls
operator<<(ostream&, rhs) and as far as I'm concerned that operator is
declared, right after the typedef. But when two compilers disagree with
me, I usually like to admit I'm wrong.
It smells like a const problem, but it must be possible to write
std::pair<const string, string> to an ostream via an iterator. (Or
perhaps it has to do with the context of the template instantiation?)
Am I expected to derive my own iterator? If so, why?
The workaround of course is std::transform with a function to convert the
pair to a string, but that's not efficient in terms of human or machine
cycles.
Many thanks,
--jkl
[snip]
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
typedef map<string,string> map_ss;
ostream&
operator<<( ostream& os, const map_ss::value_type& elem );
int
main( int argc, char *argv[] )
{
map_ss ml;
copy( ml.begin(), ml.end(), ostream_iterator<map_ss::value_type>(cout)
);
return 0;
}
[pins]
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
jklowden (3)
|
10/28/2010 9:17:08 AM |
|
James K. Lowden wrote:
> Can you tell me why the program below does not compile? GCC says:
>
> $ make mapout.o
> c++ -O2 -c mapout.cpp
> /usr/include/g++/bits/stream_iterator.h: In member function `
> std::ostream_iterator<_Tp, _CharT, _Traits>&
> std::ostream_iterator<_Tp,
>
> _CharT, _Traits>::operator=(const _Tp&) [with _Tp =
> std::pair<const std::string, std::string>, _CharT = char, _Traits
> = std::char_traits<char>]
> ':
> [...]
> mapout.cpp:21: instantiated from here
> /usr/include/g++/bits/stream_iterator.h:141: error: no match for
> 'operator<<'
> in '*this->std::ostream_iterator<std::pair<const std::string,
> std::string>,
> char, std::char_traits<char> >::_M_stream << __value'
>
> I understand that ostream_iterator::operator=(rhs) calls
> operator<<(ostream&, rhs) and as far as I'm concerned that operator
> is declared, right after the typedef. But when two compilers
> disagree with me, I usually like to admit I'm wrong.
>
> It smells like a const problem, but it must be possible to write
> std::pair<const string, string> to an ostream via an iterator. (Or
> perhaps it has to do with the context of the template
> instantiation?)
>
> Am I expected to derive my own iterator? If so, why?
>
> The workaround of course is std::transform with a function to
> convert the pair to a string, but that's not efficient in terms of
> human or machine cycles.
>
> Many thanks,
>
> --jkl
>
> [snip]
> #include <iostream>
> #include <fstream>
>
> #include <iterator>
> #include <algorithm>
> #include <map>
> #include <string>
>
> using namespace std;
>
> typedef map<string,string> map_ss;
>
> ostream&
> operator<<( ostream& os, const map_ss::value_type& elem );
>
> int
> main( int argc, char *argv[] )
> {
> map_ss ml;
>
> copy( ml.begin(), ml.end(),
> ostream_iterator<map_ss::value_type>(cout) );
>
> return 0;
> }
> [pins]
It's a lookup problem. The types involved in the output are all from namespace
std. Therefore, when needed, the compiler looks in namespace std for an
operator<<. There are a whole lot of them there, but none of these operators
work for std::pair.
The fact that you declared an additional operator<< on another namespace
(global) doesn't help, beacuse that namespace isn't searched as it is not
associated with any of the types involved.
Bo Persson
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Bo
|
10/29/2010 8:14:50 AM
|
|
James K. Lowden wrote:
> Can you tell me why the program below does not compile?
std::ostream_iterator won't find the operator<< you want to use in the
global namespace.
> I understand that ostream_iterator::operator=(rhs) calls
> operator<<(ostream&, rhs) and as far as I'm concerned that operator is
> declared, right after the typedef. But when two compilers disagree with
> me, I usually like to admit I'm wrong.
>
> It smells like a const problem, but it must be possible to write
> std::pair<const string, string> to an ostream via an iterator. (Or
> perhaps it has to do with the context of the template instantiation?)
Yes.
Here's the workaround I came up with.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <map>
#include <string>
#include <utility>
typedef std::map<std::string,std::string> map_ss;
// ostream_iterator won't find this
// in the global namespace
std::ostream &operator<<(
std::ostream& os,
const map_ss::value_type& elem
) {
// or however it should be formatted.
os << "(" << elem.first << "," << elem.second << ")";
return os;
}
// so we fake it by adding some namespace indirection.
namespace fake_pair {
struct pair {
typedef ::map_ss::value_type pair_ss;
const pair_ss &p_;
pair(const pair_ss &p) : p_(p) {}
};
std::ostream &operator<<(std::ostream &o, const pair &p) {
return ::operator<<(o,p.p_);
}
}
int main()
{
map_ss ml;
ml["hello"] = "world"; // see if something prints.
std::copy(
ml.begin(),
ml.end(),
std::ostream_iterator<fake_pair::pair>(std::cout)
);
}
FWIW the above compiles and runs with VS8 and compiles with
http://www.comeaucomputing.com/tryitout/
If need be, fake_name::pair, fake_name::operator<< and ::operator<<
above, can be made into templates.
I had to search a bit to find the problem. I googled(tm) for
ostream_iterator namespace, and I found some similar problems I'm sure
there's a better one than mine out there.
http://accu.org/index.php/journals/242 which has a nice link to
http://www.boostpro.com/writing/n1691.html
http://discuss.joelonsoftware.com/default.asp?joel.3.191244.5
It wasn't hard to convince myself that the problem is with namespaces
and lookup, I tried (code below typed from memory):
std::ostream &operator<<(std::ostream &o, const std::pair<int,int> &p) {
o << p.first << "," << p.second;
return o;
}
int main() {
std::ostream_iterator<std::pair<int,int> > os(std::cout);
os = std::pair<int,int>(1,2);
}
and also tried this example with operator<< in the xyx namespace and
then in the global namespace,
namespace xyz {
class U {};
std::ostream &operator<<(std::ostream &o, const U &) {
o << "(xyz::U)";
return o;
}
}
int main() {
std::ostream_iterator<xyz::U> os(std::cout);
os = xyz::U();
std::cout << xyz::U() << std::endl;
}
HTH
LR
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
LR
|
10/29/2010 8:15:21 AM
|
|
Am 28.10.2010 17:17, schrieb James K. Lowden:
> Can you tell me why the program below does not compile? GCC says:
[error bla-bla]
> I understand that ostream_iterator::operator=(rhs) calls
> operator<<(ostream&, rhs) and as far as I'm concerned that operator
> is declared, right after the typedef. But when two compilers
> disagree with me, I usually like to admit I'm wrong.
You are wrong ;-) First, we need to figure out, which mechanism
will be used for name-lookup:
The effects of the assignment operator used in std::copy is
specified in [lib.ostream.iterator.ops]/1:
ostream_iterator& operator=(const T& value);
1 Effects:
*out_stream << value;
if(delim != 0) *out_stream << delim;
return (*this);
The recent working draft does say the same and emphasizes
this in [global.functions]/4 by giving us the information that
we can interpret this situation to consider argument depending
lookup to find the operator<< overload. But the problem is that the
affected argument types (std::ostream, std::pair<const std::string,
std::string>) will only result in a single associated
namespace, which is namespace std. The Standard Library does
not declare an operator<< overload for pair in namespace std
and you don't either (Your declaration is part of the global
namespace), so ADL used within std::copy through operator=
of ostream_iterator wont find any match.
> It smells like a const problem, but it must be possible to write
> std::pair<const string, string> to an ostream via an iterator. (Or
> perhaps it has to do with the context of the template
> instantiation?)
From a language point of view there is no problem to define
such an operator<< for std::pair and there is also no const problem
involved in this example. But the library does not allow to add
such an operator in namespace std, see [lib.reserved.names]/1:
"It is undefined for a C++ program to add declarations or definitions to
namespace std or namespaces within namespace std unless otherwise
specified."
> Am I expected to derive my own iterator? If so, why?
No, there is no requirement for this.
> The workaround of course is std::transform with a function to
> convert the pair to a string, but that's not efficient in terms of
> human or machine cycles.
Nevertheless I recommend to follow this route, it is both portable
and non-problematic. You could define a single helper functor
that you could reuse in all other situations where you need to
perform such output. It also makes it much easier to control different
output flavors.
HTH & Greetings from Bremen,
Daniel Kr�gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
ISO
|
10/29/2010 8:15:45 AM
|
|
"James K. Lowden" <jklowden@schemamania.org> wrote in message
news:20101027221206.741eb283.jklowden@schemamania.org...
> Can you tell me why the program below does not compile? GCC says:
>
> $ make mapout.o
> c++ -O2 -c mapout.cpp
> /usr/include/g++/bits/stream_iterator.h: In member function `
> std::ostream_iterator<_Tp, _CharT, _Traits>& std::ostream_iterator<_Tp,
>
> _CharT, _Traits>::operator=(const _Tp&) [with _Tp = std::pair<const
> std::string, std::string>, _CharT = char, _Traits =
> std::char_traits<char>]
> ':
> [...]
> mapout.cpp:21: instantiated from here
> /usr/include/g++/bits/stream_iterator.h:141: error: no match for
> 'operator<<'
> in '*this->std::ostream_iterator<std::pair<const std::string,
> std::string>,
> char, std::char_traits<char> >::_M_stream << __value'
>
> I understand that ostream_iterator::operator=(rhs) calls
> operator<<(ostream&, rhs) and as far as I'm concerned that operator is
> declared, right after the typedef. But when two compilers disagree with
> me, I usually like to admit I'm wrong.
>
> It smells like a const problem, but it must be possible to write
> std::pair<const string, string> to an ostream via an iterator. (Or
> perhaps it has to do with the context of the template instantiation?)
>
> Am I expected to derive my own iterator? If so, why?
>
> The workaround of course is std::transform with a function to convert the
> pair to a string, but that's not efficient in terms of human or machine
> cycles.
>
> Many thanks,
>
> --jkl
>
> [snip]
> #include <iostream>
> #include <fstream>
>
> #include <iterator>
> #include <algorithm>
> #include <map>
> #include <string>
>
> using namespace std;
>
> typedef map<string,string> map_ss;
>
> ostream&
> operator<<( ostream& os, const map_ss::value_type& elem );
>
> int
> main( int argc, char *argv[] )
> {
> map_ss ml;
>
> copy( ml.begin(), ml.end(), ostream_iterator<map_ss::value_type>(cout)
> );
>
> return 0;
> }
> [pins]
Remember, a standard map iterator has 2 parts, a ->first and a ->second. The
->first points to the key, the ->set points to the data. In your case, ->first
would be a std::string as would ->second. I don't see this reflected in your code.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Jim
|
10/29/2010 8:31:13 AM
|
|
On Oct 28, 5:17 pm, "James K. Lowden" <jklow...@schemamania.org>
wrote:
> Can you tell me why the program below does not compile? GCC says:
>
<snip>
> I understand that ostream_iterator::operator=(rhs) calls
> operator<<(ostream&, rhs) and as far as I'm concerned that operator is
> declared, right after the typedef. But when two compilers disagree with
> me, I usually like to admit I'm wrong.
You have declared the operator alright, but it is not in a namespace
where the compiler looks for it.
The code that invokes operator<< resides in the ::std namespace, and
all the types involved are also part of the ::std namespace, so that
is the only namespace where the compiler looks for a declaration of
operator<<.
Unfortunately, your declaration will not be found, because it is in
the global namespace, which is not searched.
The solution is to declare (and define) your operator<< as a member of
namespace std.
namespace std {
ostream&
operator<<( ostream& os, const map_ss::value_type& elem );
}
This is technically UB, because you are not supposed to add overloads
of an operator to namespace std, but it is the only way the compiler
will be going to find your operator<< and there are no known compilers
that break things if you do this.
>
> Many thanks,
>
> --jkl
>
Bart v Ingen Schenau
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Bart
|
10/29/2010 8:04:07 PM
|
|
On 28/10/2010 16:17, James K. Lowden wrote:
> Can you tell me why the program below does not compile? GCC says:
>
> $ make mapout.o
> c++ -O2 -c mapout.cpp
> /usr/include/g++/bits/stream_iterator.h: In member function `
> std::ostream_iterator<_Tp, _CharT, _Traits>& std::ostream_iterator<_Tp,
>
> _CharT, _Traits>::operator=(const _Tp&) [with _Tp = std::pair<const
> std::string, std::string>, _CharT = char, _Traits =
> std::char_traits<char>]
> ':
> [...]
> mapout.cpp:21: instantiated from here
> /usr/include/g++/bits/stream_iterator.h:141: error: no match for
> 'operator<<'
> in '*this->std::ostream_iterator<std::pair<const std::string,
> std::string>,
> char, std::char_traits<char> >::_M_stream<< __value'
>
> I understand that ostream_iterator::operator=(rhs) calls
> operator<<(ostream&, rhs) and as far as I'm concerned that operator is
> declared, right after the typedef. But when two compilers disagree with
> me, I usually like to admit I'm wrong.
>
> It smells like a const problem, but it must be possible to write
> std::pair<const string, string> to an ostream via an iterator. (Or
> perhaps it has to do with the context of the template instantiation?)
>
> Am I expected to derive my own iterator? If so, why?
>
> The workaround of course is std::transform with a function to convert the
> pair to a string, but that's not efficient in terms of human or machine
> cycles.
>
> Many thanks,
>
> --jkl
>
> [snip]
> #include<iostream>
> #include<fstream>
>
> #include<iterator>
> #include<algorithm>
> #include<map>
> #include<string>
>
> using namespace std;
>
> typedef map<string,string> map_ss;
>
> ostream&
> operator<<( ostream& os, const map_ss::value_type& elem );
>
> int
> main( int argc, char *argv[] )
> {
> map_ss ml;
>
> copy( ml.begin(), ml.end(), ostream_iterator<map_ss::value_type>(cout)
> );
>
> return 0;
> }
> [pins]
There isn't an operator<< defined for std::pair. This doesn't work either:
#include <iostream>
#include <string>
#include <utility>
int main()
{
std::pair<std::string,std::string> p("Big", "Wibble");
std::cout << p << '\n';
return 0;
}
That makes a certain amount of sense, because there are many different textual
representations that people might want for a pair, e.g.
(2,3)
2 3
[2,3]
etc.
Note that even if you try and define your own operator<< for std::pair in the
global namespace, it still won't work with ostream_iterator because of the way
lookup works. You'd have to put it in namespace std (which works, but isn't
technically allowed). See here, where people have discussed this before:
http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/81414c345872699b/da7dba3406dc0fae?lnk=gst&q=ostream_iterator+pair&rnum=3
Cheers,
Stu
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Stuart
|
10/30/2010 10:45:21 AM
|
|
LR wrote:
> James K. Lowden wrote:
> > Can you tell me why the program below does not compile?
>
> std::ostream_iterator won't find the operator<< you want to use in the
> global namespace.
>
.....
> I had to search a bit to find the problem. I googled(tm) for
> ostream_iterator namespace, and I found some similar problems I'm sure
> there's a better one than mine out there.
Many thanks for your answer and others'. More than once I've been
defeated trying to grok Koenig lookup, but my question can't be answered
without understanding it. A working program (very similar to yours) is
attached.
The answer is: there are many overloads of std::operator<<, none of which
accepts a std::pair, and ADL lookup specifies that if a function name
exists in the argument's namespace, the search is constrained to that
namespace.
To write std::map via ostream_iterator, it's necessary to have some way to
write std::map::value_type, a std::pair. Either,
1. Define such an operator for namespace std. This works with many
popular compilers but is prohibited by the C++ standard.
2. Define a proxy i.e. either:
a. use std::transform to convert the pair to a type
for which a std::operator<< exists, or
b. define a type constructable from std::pair, and
an operator<< to write it to std::ostream. Use the new
type as a parameter to ostream_iterator. std::copy will
convert each map element to the new type.
(1) is tempting and *very* simple, but in the end I think (2 b) is both
portable and efficient, if unlovely. (It is interesting to note that the
new type cannot be derived from std::pair, even privately.)
[snip]
#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <map>
#include <string>
using namespace std;
typedef map<string,string> map_ss;
class pear
{
const map_ss::value_type& value;
friend ostream& operator<<( ostream&, const pear& );
public:
pear( const map_ss::value_type& value ) : value(value) {}
};
ostream&
operator<<( ostream& os, const pear& p )
{
return os << p.value.first << ":" << p.value.second;
}
int
main( int argc, char *argv[] )
{
map_ss ml;
ml.insert( make_pair("a", "one") );
copy( ml.begin(), ml.end(), ostream_iterator<pear>(cout, "\n") );
return 0;
}
[pins]
I found these links helpful:
1. http://accu.org/index.php/journals/242
2. http://www.gotw.ca/publications/mill02.htm
3. http://www.gotw.ca/publications/mill08.htm
4. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2103.pdf
(The last is Herb Sutter's proposed remedy for behavior he so ably
explains in 2 and 3.)
--jkl
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
James
|
11/1/2010 1:16:29 AM
|
|
|
7 Replies
380 Views
(page loaded in 0.126 seconds)
|