Jump to content
  • Advertisement
Sign in to follow this  
thedustbustr

templated stream operator for list

This topic is 4618 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to implement the stream insertion operator so I can std::cout a list as an object instead of per-element. I'm getting a linker error. Relevant source:
namespace dust {
template <class T> class list
{
/* ... */
friend std::ostream& operator << (std::ostream &os,const list<T>& obj);
};

template<class T>
std::ostream& operator << (std::ostream &os,const list<T>& obj)
{
	//iterate through list
	os << "[";
	for (node<T>* n=obj.head; n!=tail; n=n->next) os << *n << " ";
	n=n->next; //seperate tail so no trailing space
	os << *n << "]";
	return os;
}
}; //namespace dust
//in main
list<int> a;
std::cout << a << std::endl;

list error LNK2019: unresolved external symbol "class 
std::basic_ostream<char,struct std::char_traits<char> > & __cdecl 
dust::operator<<(class std::basic_ostream<char,struct std::char_traits<char> > 
&,class dust::list<int> const &)" 
Is my syntax wrong? I can't for the life of me resolve this error. I'm using VC71.

Share this post


Link to post
Share on other sites
Advertisement

template<T> friend std::ostream& operator<<(std::ostream &os,const list<T>& obj);

Share this post


Link to post
Share on other sites
Negative, prepending template<T> or template<class T> to the declaration just results in compile errors, including the apparent effective loss of friend functionality ('cannot access private members').

Share this post


Link to post
Share on other sites
Why does this need to be a friend function? Surely there's a publically accessable way to iterate through the list?

As to your problem, try:
friend std::ostream& operator<<(std::ostream &os,const list& obj);

If that doesn't solve your problem, then I'll have to think harder about it.

Share this post


Link to post
Share on other sites
Quote:
Original post by thedustbustr
Negative, prepending template<T> or template<class T> to the declaration just results in compile errors, including the apparent effective loss of friend functionality ('cannot access private members').


when i try your piece of code with my change in VS03 it compiles AND links without errors, so i can only assume that you put the operator << definition into a c(++) file => never do that when working with templates!

Share this post


Link to post
Share on other sites
Better way:

#include <iterator>
#include <algorithm>
#include <list>
#include <iostream>

int main() {
std::list<int> my_list;

// Give it some values
std::generate_n( &std::rand, 10, std::back_inserter(my_list) );

// Sort it
my_list.sort();

// And print it
std::copy( my_list.begin(), my_list.end(),
std::ostream_iterator<int>( std::cout, ", " ) );

// Print again, using boost::lambda
std::for_each( my_list.begin(), my_list.end(),
std::cout << _1 << ", " );
}




In any case, the printing function shouldn't need to be a friend. If the public can't get to the elements in the list, then it's not very useful, is it? :P

If it's absolutely nessesary to have access to the private members for outputting, why not make a .write_to(std::ostream&) function in the class which the operator<<( std::ostream &, List<T> const &) can call?

Share this post


Link to post
Share on other sites
Although I completely agree with me22 that you should use std::list and if you do have good reason not to use std::list that your overloaded insertion operator should not need to be a friend, for reference here is how you would make it a friend if you really needed to:
#include <iostream>

namespace dust
{

// forward declaration of list class needed for following forward declaration
template < class T >
class list;

// forward declaration of insertion operator needed to declare a specific template friend
template < class T >
std::ostream & operator<<(std::ostream & os, list< T > const & obj);

template <class T> class list
{
struct node
{
node * next;
T value;
};
node * head;
node * tail;

// note the additional <> which indicates that the friend is a template function
friend std::ostream & operator<<<>(std::ostream & os, list< T > const & obj);
};

template<class T>
std::ostream & operator<<(std::ostream & os, list< T > const & obj)
{
os << "[";
typename list< T >::node * node;
for (node = obj.head; node != obj.tail; node = node->next)
{
os << node->value << " ";
}
node = node->next;
os << node->value << "]";
return os;
}

}

int main()
{
dust::list<int> a;
std::cout << a << std::endl;
}

Note that this is not equivalent to the friend declaration template < typename T > friend std::ostream & operator<<(std::ostream & os, list< T > const & obj);, which makes all list insertion operators friends of all list template instantiations (i.e. operator<<(std::ostream &, list< int >) would be able to access private members of list< double >).

Enigma

Share this post


Link to post
Share on other sites
Just thought I'd also point out that there's also a bug in your code. The bug is that it wont work with an empty list. You need to write it more like this:
	node<T>* n = obj.head;
os << "[";
while (n != NULL)
{
os << *n;
n = n->next;
if (n != NULL)
os << " ";
}
os << "]";
I've used while loop instead, but you can use whatever you like.

Share this post


Link to post
Share on other sites
Sigh. More wheel reinvention every day around here eh?


#include <list>
#include <iostream>
#include <algorithm>

template <typename Container, typename Contained>
std::ostream& operator << (std::ostream& os, const Container<Contained>& c) {
os << '[';
std::copy(c.begin(), c.end(), std::ostream_iterator<Contained>(os, " "));
return os << ']';
}


Should work; I don't have access to a C++ compiler right at the moment :/ This is designed to work with any STL container type (which you should be using, e.g. std::list from <list>).

Share this post


Link to post
Share on other sites
Zahlman's syntax is slightly off, plus that doesn't quite do exactly what the OP wanted (it inserts an additional space character before the closing square bracket) but it's a good starting point. Assuming you can assume bidirectional iterators then you could modify it to:
#include <algorithm>
#include <iostream>
#include <iterator>

template < template < typename T > class Container, typename Contained >
std::ostream & operator<<(std::ostream & os, const Container< Contained > & c)
{
os << '[';
if (c.empty())
{
// if you want any special case for the empty list
}
else
{
typename Container< Contained >::const_iterator endButOne = c.end();
--endButOne;
std::copy(c.begin(), endButOne, std::ostream_iterator< Contained >(os, " "));
os << *endButOne;
}
return os << ']';
}


Enigma

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!