Board index » cppbuilder » Need help with friends of class templates

Need help with friends of class templates


2004-01-22 01:14:31 PM
cppbuilder108
Hi all,
I wrote a smart pointer class that uses reference counting for my own
learning purposes but I'm running into a bit of trouble overloading
operator << as a friend. This is what the source looks like:
mysmart_ptr.h:
#ifndef _MYSMART_PTR_H
#define _MYSMART_PTR_H
#include <iostream>
#include <map>
template <typename T>
class mysmart_ptr
{
public:
mysmart_ptr();
mysmart_ptr(T* const ptr);
mysmart_ptr(const mysmart_ptr &rhs);
~mysmart_ptr();
//mysmart_ptr deferencing operators
T& operator *();
const T& operator *() const;
T* operator ->();
const T* operator ->() const;
operator bool() const;
operator T*() const;
bool operator ==(const mysmart_ptr &rhs) const;
bool operator !=(const mysmart_ptr &rhs) const;
const mysmart_ptr& operator =(const mysmart_ptr &rhs);
friend std::ostream& operator <<(std::ostream &os, const
mysmart_ptr &rhs);
static unsigned short ctor;
static unsigned short dtor;
private:
static std::map<T *, unsigned short>m_refcount;
T *m_ptr;
};//mysmart_ptr
#include "mysmart_ptr.cpp"
#endif
mysmart_ptr.cpp:
#include "mysmart_ptr.h"
//all the other member functions & initalization goes here
template <typename T>
std::ostream& operator <<(std::ostream &os, const mysmart_ptr<T>&rhs)
{
os << rhs.m_ptr;
return os;
}//operator <<
Everything compiles but the linker complains:
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
Error: Unresolved external 'operator>&, const mysmart_ptr&)'
referenced from E:\BORLAND\MYWORKSPACE\BIN\TEST2.OBJ
Error: Unresolved external 'operator>&, const mysmart_ptr&)'
referenced from E:\BORLAND\MYWORKSPACE\BIN\TEST2.OBJ
From the error I'm thinking some how the compiler isn't creating any
corresponding functions from my templated operator <<. Any idea what the
problem is here and why it doesn't work?
However, so far I have been about to find two workarounds for this. One
is to overload << twice. Once as a member function and a second time as a
non-member taking a stream and mysmart_ptr as parameter. When the non-
member operator << gets invoked it just passes control into the member
operator << in the correct format etc.
The second workaround was to define friend operator << right inside my
template class definition.
Thought both are workable, neither are really satisfactory. Especially
with the second workaround, if I have to put more friend functions into
this template class things can get really yucky. So any suggestions and
comments about this problem I'm having?
Thanks
 
 

Re:Need help with friends of class templates

wow, that's fast feedback :D
XXXX@XXXXX.COM (Thomas Maeder [TeamB]) wrote in
Quote

Identifiers starting _[A-Z] and/or containing __ are reserved to the
C++ implementation; we mere users must not declare them in our code.

Would MYSMART_PTR_H be better or do you recommend following a different
convention?
Quote

Without looking at the semantics of mysmart_ptr, it's probably safe to
mention that it needs a user-written copy-assignment operator as well.


Do you mean these two member operators?
//copy constructor
mysmart_ptr(const mysmart_ptr &rhs);
//assignment operator
const mysmart_ptr& operator =(const mysmart_ptr &rhs);
Quote


The cure is to prevent the declaration of the non-template version.
This goes like this:

template <typename T>
class mysmart_ptr;

template <typename T>
std::ostream& operator<<(std::ostream &, mysmart_ptr<T>const &);

template <typename T>
class mysmart_ptr
{
// ...

friend std::ostream& operator<< <>(std::ostream &,
mysmart_ptr const &);
};

Note the <>in the friend declaration. It causes the appropriate
specialization of the previously declared operator<< template to
become a friend.
okie I got it working but that is some tricky template semantics there.
Can you suggest any hints or tips I can use as guidelines when using
templates so I can avoid these tricky semantics? Or maybe some way to
spot them easier at least so I can correct them when they do crop up.
Thanks again for the fast response :D
 

Re:Need help with friends of class templates

Vivi Orunitia < XXXX@XXXXX.COM >writes:
Quote
Hi all,

I wrote a smart pointer class that uses reference counting for my own
learning purposes but I'm running into a bit of trouble overloading
operator << as a friend. This is what the source looks like:

mysmart_ptr.h:

#ifndef _MYSMART_PTR_H
#define _MYSMART_PTR_H
Identifiers starting _[A-Z] and/or containing __ are reserved to the C++
implementation; we mere users must not declare them in our code.
Quote
#include <iostream>
#include <map>

template <typename T>
class mysmart_ptr
{
public:
mysmart_ptr();
mysmart_ptr(T* const ptr);
mysmart_ptr(const mysmart_ptr &rhs);
~mysmart_ptr();
Without looking at the semantics of mysmart_ptr, it's probably safe to
mention that it needs a user-written copy-assignment operator as well.
[snip]
Quote
friend std::ostream& operator <<(std::ostream &os, const
mysmart_ptr &rhs);
This declares a non-template operator<< a friend. Since this operator wasn't
known before, it's implicitly declared at this point as well.
Quote
};//mysmart_ptr

#include "mysmart_ptr.cpp"
#endif

mysmart_ptr.cpp:

#include "mysmart_ptr.h"
//all the other member functions & initalization goes here

template <typename T>
std::ostream& operator <<(std::ostream &os, const mysmart_ptr<T>&rhs)
{
os << rhs.m_ptr;
return os;
}//operator <<
This defines a previously undeclared operator<< template.
Quote
Everything compiles but the linker complains:
An expression such as
std::cout << myptr
involves an overload resolution. In this case, the non-template version
declared in the friend declaration wins. The linker complains because there
is no definition for that non-template operator.
The cure is to prevent the declaration of the non-template version. This
goes like this:
template <typename T>
class mysmart_ptr;
template <typename T>
std::ostream& operator<<(std::ostream &, mysmart_ptr<T>const &);
template <typename T>
class mysmart_ptr
{
// ...
friend std::ostream& operator<< <>(std::ostream &,
mysmart_ptr const &);
};
Note the <>in the friend declaration. It causes the appropriate
specialization of the previously declared operator<< template to become a
friend.
 

{smallsort}

Re:Need help with friends of class templates

Vivi Orunitia < XXXX@XXXXX.COM >writes:
Quote
>Identifiers starting _[A-Z] and/or containing __ are reserved to the
>C++ implementation; we mere users must not declare them in our code.

Would MYSMART_PTR_H be better or do you recommend following a different
convention?
Much better.
Since _MYSMART_PTR_H is reserved to the implementation, a Standard Library
algorithm could use this name for a local variable. Now imagine what happens
if you #include the header containing that algorithm in mysmart_ptr.h; you'd
get very strange compiler error messages.
This can't happen if you use #include guards of them form MYSMART_PTR_H.
Quote
>Without looking at the semantics of mysmart_ptr, it's probably safe to
>mention that it needs a user-written copy-assignment operator as well.

Do you mean these two member operators?

//copy constructor
mysmart_ptr(const mysmart_ptr &rhs);
This is not an operator, but the copy constructor. It already shows up in
the code you gave in the post that started this thread and helped leading me
to the conclusion that the copy-assignment operator is probably needed.
Quote
//assignment operator
const mysmart_ptr& operator =(const mysmart_ptr &rhs);
This is what I meant, yes. As a side note, it is always a good idea to do
as the Romans do if you are in Rome. In C++, this becomes "do as the ints
do". This means that the returrn type of the copy-assignment operator
should not be a reference to const, but a reference to non-const:
mysmart_ptr& operator =(const mysmart_ptr &rhs);
Quote
okie I got it working but that is some tricky template semantics there.
Can you suggest any hints or tips I can use as guidelines when using
templates so I can avoid these tricky semantics? Or maybe some way to
spot them easier at least so I can correct them when they do crop up.
Templates are hard to program.
One thing you should learn is to understand your compiler. C++BuilderX gives
Error: Unresolved external 'operator <<(_STL::basic_ostream<char, _STL::char_traits<char>>&, const mysmart_ptr<int>&)' referenced from H:\SMART_PTR.OBJ
, and I assume that C++ Builder doesn't produce anything much different from
that. If it were a template whose definition was missing, the error message
would look something like this:
Error: Unresolved external 'mysmart_ptr<int>::~mysmart_ptr<int>()' referenced from H:\SMART_PTR.OBJ
Error: Unresolved external 'mysmart_ptr<int>::mysmart_ptr<int>()' referenced from H:\SMART_PTR.OBJ
With a little imagination while reading, you could have asked yourself why
the linker is looking for a non-template since you provided a template.
Another idea is to use a different compiler that may give a more intelligible
diagnostic message. gcc is particularly gifted for this kind of error; it
warns at compile time already:
smart_ptr.cpp:27: warning: friend declaration `std::ostream&
operator<<(std::ostream&, const mysmart_ptr<T>&)' declares a non-template
function
smart_ptr.cpp:27: warning: (if this is not what you intended, make sure the
function template has already been declared and add <>after the function
name here) -Wno-non-template-friend disables this warning