STL friendly operator << doesn't work
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 17

Thread: STL friendly operator << doesn't work

  1. #1
    Join Date
    Feb 2012
    Posts
    5

    Lightbulb 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

  2. #2
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,263

    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.
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  3. #3
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Fairfax, VA
    Posts
    10,888

    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.

  4. #4
    Join Date
    Feb 2012
    Posts
    5

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by laserlight View Post
    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?).

    Quote Originally Posted by Lindley View Post
    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?

  5. #5
    Lindley is offline Elite Member Power Poster
    Join Date
    Oct 2007
    Location
    Fairfax, VA
    Posts
    10,888

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by rodis View Post
    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.

  6. #6
    Join Date
    Feb 2012
    Posts
    5

    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?

  7. #7
    Join Date
    Feb 2012
    Posts
    5

    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).

  8. #8
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,263

    Re: STL friendly operator << doesn't work

    Quote 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.

    Quote 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?
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  9. #9
    Join Date
    Jul 2005
    Location
    Netherlands
    Posts
    2,012

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by rodis View Post
    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

  10. #10
    Join Date
    Jul 2002
    Location
    Portsmouth. United Kingdom
    Posts
    2,704

    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

  11. #11
    Join Date
    Jan 2006
    Location
    Singapore
    Posts
    6,263

    Re: STL friendly operator << doesn't work

    Quote 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.
    C + C++ Compiler: MinGW port of GCC
    Build + Version Control System: SCons + Bazaar

    Look up a C/C++ Reference and learn How To Ask Questions The Smart Way
    Kindly rate my posts if you found them useful

  12. #12
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,290

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by rodis View Post
    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 LITE 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.

  13. #13
    Join Date
    Jul 2002
    Location
    Portsmouth. United Kingdom
    Posts
    2,704

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by laserlight View Post
    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

  14. #14
    Join Date
    Oct 2008
    Posts
    1,110

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by monarch_dodra View Post
    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 )
    {
    	// ...

  15. #15
    Join Date
    Jun 2009
    Location
    France
    Posts
    2,290

    Re: STL friendly operator << doesn't work

    Quote Originally Posted by superbonzo View Post
    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 LITE 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.

Page 1 of 2 12 LastLast

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
  •  


Azure Activities Information Page

Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center