-
February 13th, 2012, 12:41 PM
#1
STL friendly operator << doesn't work
I want to overload ostream& operator << so that it prints content of whatever container I want. I wrote something like this:
Code:
#include <iostream>
#include <list>
#include <set>
template <class T>
ostream& operator << (ostream& strm, T l)
{
for (class T::iterator it = l.begin(); it != l.end();++it)
{
strm << *it;
// if (++it != l.end())
// strm << " ";
}
return strm;
}
int main()
{
list<int> l;
int t[] = {1,2,3,-1,-2,-3};
l.insert(l.begin(), t, t + 6);
cout << l;
set<int> s;
s.insert(l.begin(), l.end());
cout << s;
}
It works. However, it'd be nice to actually have these spaces between numbers. There's the problem: when I uncomment the code (and remove ++it from the for loop), the compilers gives me a bunch of messages with the "main" reading:
Code:
stl_test.cpp: In function ‘std::ostream& operator<<(std::ostream&, T)’:
stl_test.cpp:19: error: ambiguous overload for ‘operator<<’ in ‘strm << " "’
Does anyone have any idea why does it not work? (it seems like << isn't overloaded for const char *, but simple cout << " "; works, so...)
I use g++ 4.4.5
-
February 13th, 2012, 12:54 PM
#2
Re: STL friendly operator << doesn't work
Perhaps the problem is precisely because you are overloading operator<<: are you trying to call the operator<< that is already overloaded for null terminated strings, or are you trying to call this operator<< by instantiating the template with T = const char[2]?
You might be able to fix this by writing:
Code:
std::operator<<(strm, " ");
though that still leaves you with the bug in which you increment it twice on each iteration.
By the way, a more conventional way to do what you want to do is to #include <algorithm> and <iterator> and write:
Code:
std::copy(l.begin(), l.end(), std::ostream_iterator<int>(std::cout, " "));
std::copy(s.begin(), s.end(), std::ostream_iterator<int>(std::cout, " "));
Also, I note that class T::iterator it should be typename T::iterator it, and that you did not fully qualify names when you should. The container should also be passed by const reference.
-
February 13th, 2012, 01:06 PM
#3
Re: STL friendly operator << doesn't work
The problem is that your operator template is not restricted to containers; it will attempt to match anything you place on the right-hand side. You might be able to use SFINAE to restrict this to containers.
-
February 13th, 2012, 02:27 PM
#4
Re: STL friendly operator << doesn't work
Originally Posted by laserlight
Perhaps the problem is precisely because you are overloading operator<<: are you trying to call the operator<< that is already overloaded for null terminated strings, or are you trying to call this operator<< by instantiating the template with T = const char[2]?
You might be able to fix this by writing:
Code:
std::operator<<(strm, " ");
though that still leaves you with the bug in which you increment it twice on each iteration.
By the way, a more conventional way to do what you want to do is to #include <algorithm> and <iterator> and write:
Code:
std::copy(l.begin(), l.end(), std::ostream_iterator<int>(std::cout, " "));
std::copy(s.begin(), s.end(), std::ostream_iterator<int>(std::cout, " "));
Also, I note that class T::iterator it should be typename T::iterator it, and that you did not fully qualify names when you should. The container should also be passed by const reference.
OK, why class in class T::iterator should be changed to typename? It works both ways I guess (just as one can write
Code:
template <typename T>
.
I forgot to copy "using namespace std;" (it's a small source).
I fully agree about that const reference.
I don't want to use these <algorithm> stuff (which is really neat, by the way), as I am currently in the process of learning how to use templates correctly.
It gets wierder as I define
Code:
const char* space = " ";
and then write operator << (strm, space); (with simple << " "; it said
Code:
call of overloaded ‘operator<<(std::basic_ostream<char, std::char_traits<char> >&, const char [2])’ is ambiguous
, but now it doesn't even like the const char *& version (why reference here?).
Originally Posted by Lindley
The problem is that your operator template is not restricted to containers; it will attempt to match anything you place on the right-hand side. You might be able to use SFINAE to restrict this to containers.
I know it is not restricted to containers, but when compiler seeks function to call, first it checks among functions that already exist, right? And I guess I can safely assume, operator << (sth, const char *) should already be there, shouldn't it...?
You guys have any more detailed suggestions on how I should make it work?
-
February 13th, 2012, 02:34 PM
#5
Re: STL friendly operator << doesn't work
Originally Posted by rodis
I know it is not restricted to containers, but when compiler seeks function to call, first it checks among functions that already exist, right? And I guess I can safely assume, operator << (sth, const char *) should already be there, shouldn't it...?
Normally, yes. It's possible the behavior might be different when used "recursively" as you are doing, I'm not sure.
-
February 13th, 2012, 02:48 PM
#6
Re: STL friendly operator << doesn't work
Code:
stl_test.cpp: In function ‘std::ostream& operator<<(std::ostream&, T)’:
stl_test.cpp:19: error: call of overloaded ‘operator<<(std::basic_ostream<char, std::char_traits<char> >&, const char*&)’ is ambiguous
stl_test.cpp:13: note: candidates are: std::ostream& operator<<(std::ostream&, T) [with T = const char*]
/usr/include/c++/4.4/ostream:505: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.4/bits/ostream.tcc:321: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/include/c++/4.4/ostream:488: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>]
stl_test.cpp: In function ‘std::ostream& operator<<(std::ostream&, T) [with T = std::list<int, std::allocator<int> >]’:
stl_test.cpp:39: instantiated from here
stl_test.cpp:19: error: call of overloaded ‘operator<<(std::basic_ostream<char, std::char_traits<char> >&, const char*&)’ is ambiguous
stl_test.cpp:13: note: candidates are: std::ostream& operator<<(std::ostream&, T) [with T = const char*]
/usr/include/c++/4.4/ostream:505: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.4/bits/ostream.tcc:321: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/include/c++/4.4/ostream:488: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>]
stl_test.cpp: In function ‘std::ostream& operator<<(std::ostream&, T) [with T = std::set<int, std::less<int>, std::allocator<int> >]’:
stl_test.cpp:42: instantiated from here
stl_test.cpp:19: error: call of overloaded ‘operator<<(std::basic_ostream<char, std::char_traits<char> >&, const char*&)’ is ambiguous
stl_test.cpp:13: note: candidates are: std::ostream& operator<<(std::ostream&, T) [with T = const char*]
/usr/include/c++/4.4/ostream:505: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.4/bits/ostream.tcc:321: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/include/c++/4.4/ostream:488: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>]
It sees "std:stream& operator<<(std:stream&, T) [with T = const char*]" but this template class basic_ostream is causing all the trouble. Anyone knows how to silence it?
-
February 13th, 2012, 04:05 PM
#7
Re: STL friendly operator << doesn't work
OK, this is peculiar, but
Code:
ostream& operator << (ostream& strm, const char* s)
{
static string tmpstr;
tmpstr = s;
strm << tmpstr;
return strm;
}
solves the problem, so iostream doesn't, indeed, have this version of operator overloaded and that (printing const char *) worked because this was converted to std::string (this is only my guess).
-
February 13th, 2012, 08:43 PM
#8
Re: STL friendly operator << doesn't work
Originally Posted by rodis
OK, why class in class T::iterator should be changed to typename? It works both ways I guess (just as one can write
Code:
template <typename T>
No, this is one case in which they are not interchangeable. My guess is that we're looking at the relic from C where you have to use struct T if you don't have an appropriate typedef, i.e., the class keyword here merely specifies that T is a class, whereas you want to specify that T::iterator is a type name.
Originally Posted by rodis
You guys have any more detailed suggestions on how I should make it work?
So you're saying that my suggestion of fully qualifying the operator<< call did not work?
-
February 14th, 2012, 05:15 AM
#9
Re: STL friendly operator << doesn't work
Originally Posted by rodis
so iostream doesn't, indeed, have this version of operator overloaded and that (printing const char *) worked because this was converted to std::string (this is only my guess).
That's not what your compiler is telling you.
Code:
stl_test.cpp:13: note: candidates are: std::ostream& operator<<(std::ostream&, T) [with T = const char*]
/usr/include/c++/4.4/ostream:505: note: std::basic_ostream<char, _Traits>& std::operator<<(std::basic_ostream<char, _Traits>&, const char*) [with _Traits = std::char_traits<char>]
/usr/include/c++/4.4/bits/ostream.tcc:321: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const char*) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/include/c++/4.4/ostream:488: note: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>]
The second candidate is the overload for const char*. However, it is templated on the traits class of the stream. So the compiler has to pick between several template functions. It cannot do that in this case. The only way to help the compiler is to either avoid writing an overloaded template function (as suggested by laserlight) or to use SFINAE (as suggested by Lindley). However, the latter requires a descend understanding of overload resolution, which is exactly what's going on here. So I would suggest reading up on the topic before using SFINAE. In any case, don't just guess what the compiler is doing.
Cheers, D Drmmr
Please put [code][/code] tags around your code to preserve indentation and make it more readable.
As long as man ascribes to himself what is merely a posibility, he will not work for the attainment of it. - P. D. Ouspensky
-
February 15th, 2012, 04:18 AM
#10
Re: STL friendly operator << doesn't work
Another alternative, if you don't mind writing an overloaded function for each container type.
(though that may negate the intention of the exercise)
Code:
template <typename T>
std::ostream& operator << (std::ostream& strm, const std::vector<T>& v)
{
std::copy(v.begin(), v.end(), std::ostream_iterator<T>(strm, " "));
}
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
February 15th, 2012, 04:29 AM
#11
Re: STL friendly operator << doesn't work
Originally Posted by JohnW@Wessex
Another alternative, if you don't mind writing an overloaded function for each container type.
A catch is that there are more template parameters than just the element type (e.g., allocator for std::vector) so you'll need to allow for them too, and from what I understand, standard library implementations might also have additional template parameters that could be problematic.
-
February 15th, 2012, 05:00 AM
#12
Re: STL friendly operator << doesn't work
Originally Posted by rodis
I wrote something like this:
It helps if you post the exact code. YOur code did not compile because you forgot the "using namespace std;" declaration I suppose was in your code. It is just a detail, but it also means I can't trust your code is the one that recreates the error.
Anyways, you could always use the poor man's SFISNAE:
Code:
#include <iostream>
#include <list>
#include <set>
template <class T, class T2 = typename T::iterator>
std::ostream& operator << (std::ostream& strm, const T& l)
{
for (typename T::const_iterator it = begin(l); it != end(l); ++it)
{
strm << *it;
if (++it != l.end())
strm << " ";
}
return strm;
}
int main()
{
std::list<int> l;
int t[] = {1,2,3,-1,-2,-3};
l.insert(l.begin(), t, t + 6);
std::cout << l;
std::set<int> s;
s.insert(l.begin(), l.end());
std::cout << s;
}
----
That said, I'd go with Laserlights' very first recommendation, and not write this: There are explicit customizable algorithmic functionality that allow you to do the same thing. Your approach has 2 major problems:
1. What if you want to print the container, but with commas instead of spaces? What if you want to only print half the container? You are providing a closed functionality.
2. Nobody expects this function to exist. On the long run, it will create confusion, and more harm than good.
Now, if you insist on having it, I'd consider making it a named "print_container_on". You'll get the same functionality, without all the ambiguity.
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
-
February 15th, 2012, 05:49 AM
#13
Re: STL friendly operator << doesn't work
Originally Posted by laserlight
A catch is that there are more template parameters than just the element type
Good point. It's sometimes easy to forget that.
"It doesn't matter how beautiful your theory is, it doesn't matter how smart you are. If it doesn't agree with experiment, it's wrong."
Richard P. Feynman
-
February 15th, 2012, 06:16 AM
#14
Re: STL friendly operator << doesn't work
Originally Posted by monarch_dodra
Anyways, you could always use the poor man's SFISNAE:
Code:
template <class T, class T2 = typename T::iterator>
std::ostream& operator << (std::ostream& strm, const T& l)
you cannot specify default template paramaters for functions. An alternative solution could be
Code:
template < class T, class V = typename T::iterator >
struct ostreamable
{
typedef std::ostream& type;
};
template <class T>
typename ostreamable<T>::type
operator << (std::ostream& strm, const T& l )
{
// ...
-
February 15th, 2012, 12:04 PM
#15
Re: STL friendly operator << doesn't work
Originally Posted by superbonzo
you cannot specify default template paramaters for functions. An alternative solution could be
Code:
template < class T, class V = typename T::iterator >
struct ostreamable
{
typedef std::ostream& type;
};
template <class T>
typename ostreamable<T>::type
operator << (std::ostream& strm, const T& l )
{
// ...
Hum... you are correct for C++03, but it would appear their support was added to C++11 (according to the websites I just surfed on). I could be mistaken.
This was my error message from GCC in C++03 mode:
error: default template arguments may not be used in function templates without -std=c++0x or -std=gnu++0x
Compiled fine in C++11 mode
Is your question related to IO?
Read this C++ FAQ article at parashift by Marshall Cline. In particular points 1-6.
It will explain how to correctly deal with IO, how to validate input, and why you shouldn't count on "while(!in.eof())". And it always makes for excellent reading.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|