Friend functions and template classes

A

Adam Parkin

Hello, all I'm having a problem with friend functions in a templatized Queue
class I'm writing using linked lists. The problem is that I can't get the
friend function to be able to access private data from the class. Here's
the gist of the code:

template <class T>
struct NodeType
{
T data;
NodeType<T> * link;
};

template <class T>
class Queue1
{
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs)
{
NodeType <T> * pIterator = rhs.tail;
return &lhs;
}

The problem I get is that in the operator<< function my compiler (Visual C++
6) is reporting:

'tail' : cannot access private member declared in class 'Queue1<char>'

(this is of course using a driver program that declares a Queue1<char>
object).

Any suggestions would be appreciated.

Adam
 
D

Denis Remezov

Adam said:
[snip]

template <class T>
class Queue1
{
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs)
{
NodeType <T> * pIterator = rhs.tail;
return &lhs;
}

As Victor said, changing
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);
to
friend ostream & operator<< <> (ostream & lhs, const Queue1 <T> & rhs);

(you can write <> instead of <T> here) would solve the problem with the
right compiler. Here is why: your definition of operator<< is a function
template; its friend declaration, if unqualified, must be a template-id
(just what you probably meant to do).
Any suggestions would be appreciated.

0. Get a better compiler.

There are a couple of alternatives that you could try as well.

1. Remember,
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);

is a declaration of a function, not a function template, despite the <T>
in the argument list. That means that the compiler will need a corresponding
definition. Replace your definition of operator<< with the following
(but keep your original friend declaration):

ostream & operator<< (ostream & lhs, const Queue1 <char> & rhs)
{
NodeType <char> * pIterator = rhs.tail;
return lhs;
}

Note that this is not a template specialisation. I wouldn't like doing it
this way, but it is still standard, and maybe VC++6 will accept it.


2. Finally, you may try the following: qualify the declaration of the friend
operator<< with :: and predeclare it (rules are different for qualified names
in this case):

template <class T>
class Queue1;

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);

template <class T>
class Queue1
{
friend ostream & ::eek:perator<< <> (ostream & lhs, const Queue1 <T> & rhs);
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};


Denis
 
V

Victor Bazarov

Adam Parkin said:
Hello, all I'm having a problem with friend functions in a templatized Queue
class I'm writing using linked lists. The problem is that I can't get the
friend function to be able to access private data from the class. Here's
the gist of the code:

template <class T>
struct NodeType
{
T data;
NodeType<T> * link;
};


I think you need to do a bit of a declaration dance. Put here:

template<class T> class Queue1;
template said:
template <class T>
class Queue1
{
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);

Change this to

friend ostream& operator said:
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs)
{
NodeType <T> * pIterator = rhs.tail;
return &lhs;

You probably meant

return lhs;

here. BTW, avoid typing your program directly into a posting.
}

The problem I get is that in the operator<< function my compiler (Visual C++
6) is reporting:

'tail' : cannot access private member declared in class 'Queue1<char>'

(this is of course using a driver program that declares a Queue1<char>
object).

---------------------------------------------- This code
#include <iostream>
using namespace std;

template <class T>
struct NodeType
{
T data;
NodeType<T> * link;
};

template<class T> class Queue1;
template<class T> ostream& operator <<(ostream&, const Queue1<T>&);

template <class T>
class Queue1
{
friend ostream & operator<< <T>(ostream &, const Queue1&);
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs)
{
NodeType <T> * pIterator = rhs.tail;
return lhs;
}

int main()
{
Queue1<int> qi;
cout << qi;
return 0;
}

----------------------------------------------
compiles fine with Comeau and Intel. VC++ v6 refuses to compile it (due
to its very poor template handling abilities). A quick solution to this
would be a public member function

ostream& print(ostream& out) const
{
// do what you need
return out;
}

in the Queue1 template. The template output operator would call it:

template<class T> ostream& operator << (ostream& os, const Queue1<T>& t)
{
return t.print(os);
}
------------------------------- Full code that compiles with VC++
#include <iostream>
using namespace std;

template <class T>
struct NodeType
{
T data;
NodeType<T> * link;
};

template <class T>
class Queue1
{
public:
ostream & print(ostream & out) const
{
NodeType<T> *pIt = tail;
return out;
}
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};

template<class T>
ostream & operator<<(ostream & lhs, const Queue1<T> & rhs)
{
return rhs.print(lhs);
}

int main()
{
Queue1<int> qi;

cout << qi;

return 0;
}
 
V

Victor Bazarov

Denis Remezov said:
Adam said:
[snip]

template <class T>
class Queue1
{
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);
// some public methods & stuff here
private:
NodeType <T> * head;
NodeType <T> * tail;
};

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs)
{
NodeType <T> * pIterator = rhs.tail;
return &lhs;
}

As Victor said, changing
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);
to
friend ostream & operator<< <> (ostream & lhs, const Queue1 <T> & rhs);

(you can write <> instead of <T> here) would solve the problem with the
right compiler. Here is why: your definition of operator<< is a function
template; its friend declaration, if unqualified, must be a template-id
(just what you probably meant to do).
Any suggestions would be appreciated.

0. Get a better compiler.

There are a couple of alternatives that you could try as well.

1. Remember,
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);

is a declaration of a function, not a function template, despite the <T>
in the argument list. That means that the compiler will need a corresponding
definition. Replace your definition of operator<< with the following
(but keep your original friend declaration):

ostream & operator<< (ostream & lhs, const Queue1 <char> & rhs)
{
NodeType <char> * pIterator = rhs.tail;
return lhs;
}

Note that this is not a template specialisation. I wouldn't like doing it
this way, but it is still standard, and maybe VC++6 will accept it.


2. Finally, you may try the following: qualify the declaration of the friend
operator<< with :: and predeclare it (rules are different for qualified names
in this case):

template <class T>
class Queue1;

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);

template <class T>
class Queue1
{
friend ostream & ::eek:perator<< <> (ostream & lhs, const Queue1 <T> & rhs);

VC++ chokes on this. I gave a solution, and of course, your option 0
is just fine too.

Victor
 
A

Adam Parkin

Denis Remezov said:
As Victor said, changing
friend ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);
to
friend ostream & operator<< <> (ostream & lhs, const Queue1 <T> & rhs);

(you can write <> instead of <T> here) would solve the problem with the
right compiler.

And of course VC6 is not the right compiler, as this doesn't work.
0. Get a better compiler.

LOL, thanks. :p
is a declaration of a function, not a function template, despite the <T>
in the argument list. That means that the compiler will need a corresponding
definition. Replace your definition of operator<< with the following
(but keep your original friend declaration):

ostream & operator<< (ostream & lhs, const Queue1 <char> & rhs)
{

Hehe, funny enough, VC6 reports this as "binary '<<' : no operator defined
which takes a right-hand operand of type 'class Queue1<char>' (or there is
2. Finally, you may try the following: qualify the declaration of the friend
operator<< with :: and predeclare it (rules are different for qualified names
in this case):

template <class T>
class Queue1;

template <class T>
ostream & operator<< (ostream & lhs, const Queue1 <T> & rhs);

Yeah, I actually found this code on a few websites, but no, it doesn't work
under VC6 (or at least didn't for me). Thanks for the help.

Adam
 
A

Adam Parkin

Okay, figured out the problem, although I'm not entirely sure why it caused
a problem. My driver.cpp had:

#include "queue1.h"
#include <iostream>
using namespace std;

in it, and once I changed this to:

#include "queue1.h"
#include <iostream>
using std::cout;
using std::endl;

Everything worked fine, as the access of the private data member in
operator<< was now legal. Can somebody explain why changes in one module
(the Queue1.h file) were affected the way they were by the driver.cpp file?
Is it that there's something in the std namespace which conflicted with my
Queue1 module?

I suppose this is a lesson in don't be lazy, use scope resolution to be as
precise as possible.

Adam
 
V

Victor Bazarov

Adam Parkin said:
Okay, figured out the problem, although I'm not entirely sure why it caused
a problem. My driver.cpp had:

#include "queue1.h"
#include <iostream>
using namespace std;

in it, and once I changed this to:

#include "queue1.h"
#include <iostream>
using std::cout;
using std::endl;

Everything worked fine, as the access of the private data member in
operator<< was now legal. Can somebody explain why changes in one module
(the Queue1.h file) were affected the way they were by the driver.cpp file?
Is it that there's something in the std namespace which conflicted with my
Queue1 module?

I suppose this is a lesson in don't be lazy, use scope resolution to be as
precise as possible.

I don't have an answer to your question about the namespace and the
effect of it for the code, but I recommend asking the same question
in microsoft.public.vc.language. Of course, keep in mind that they
have been enjoying VC++ .NET probably longer on average than we here,
so they may be tempted to simply say, "it's fixed in v7.1, so get it
and the problem will go away"...

Victor
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top