We just upgraded from a quite old VC++ to 2005. In the process, something
has stopped working that seems like it's completely legal. Basically we have
our own very large framework. We have the usual streaming classes (base
classes for in and out streams, from which various specific streaming
classes are derived for files, memory, etc...) and the usual
templatized collection classes. We want our collections to be streamable,
where the instantiation element supports it, so we provide the usual global
(friend) streaming operators with each collection type, so that
streamability isn't forced on the instantiation element types, something
like:

//
// A templatized collection class with friend declarations for
// the streaming operators
//
template <class T> class TFundStack : public TObject
{
public :
.......

protected :
friend TBinOutStream& operator<<
(
TBinOutStream& strmOut
, const TFundStack<T>& fcolToStream
);

friend TBinInStream& operator>>
(
TBinInStream& strmIn
, TFundStack<T>& fcolToStream
);
}

// In the same header, global streaming operators
template <class T> TBinInStream&
operator>>(TBinInStream& strmToReadFrom, TFundStack<T>& colToStream)
{
}

template <class T> TBinOutStream&
operator<<(TBinOutStream& strmToWriteTo, const TFundStack<T>& colToStream)
{
}

* TBinInStream and TBinOutStream are the base classes from which various
types of binary in/out streams are derived.

// In a test app, I'd create a stack, put something in it
TFundStack<tCIDLib::TCard4> fcolStack(4);
fcolStack.Push(1);

// Then create an memory based output stream and stream the stack
TBinMBufOutStream strmTestOut(8192, 8192);
strmTest << fcolStack << kCIDLib::FlushIt;


The compiler doesn't find the global operators and I get an unresolved
symbol error on operator<<(TBinOutStream&, TFundStack<tCIDLib::TCard4>), so
it's not making the connection from the streaming of the stack to the
provided global streaming operator.

Is there something I'm missing here? Is this no longer valid in C++ or is
this some quirk of VC++ 2005, or is there some new switch I need or some
such thing?

-------------------------------------
Dean Roddey
Chairman/CTO, Charmed Quark Systems
www.charmedquark.com

Re: A change from 2002 to 2005 wrt to global templatized operators? by Igor

Igor
Sun Sep 24 16:17:57 CDT 2006

"Dean Roddey" <droddey@charmedquark.com> wrote in message
news:eSfudpB4GHA.4764@TK2MSFTNGP05.phx.gbl
> template <class T> class TFundStack : public TObject
> {
> public :
> .......
>
> protected :
> friend TBinOutStream& operator<<
> (
> TBinOutStream& strmOut
> , const TFundStack<T>& fcolToStream
> );

This befriends a non-template function.

> // In the same header, global streaming operators
> template <class T> TBinOutStream&
> operator<<(TBinOutStream& strmToWriteTo, const TFundStack<T>&
> colToStream) {
> }

This is a template function, and it is _not_ declared a friend of
TFundStack. If you want to befriend this one, the correct declaration
would be

friend TBinOutStream& operator<< <T>
(
TBinOutStream& strmOut
, const TFundStack<T>& fcolToStream
);

Note that, for this to work, operator<< <T> template must be at least
forward-declared before TFundStack.

> The compiler doesn't find the global operators and I get an unresolved
> symbol error on operator<<(TBinOutStream&,
> TFundStack<tCIDLib::TCard4>), so it's not making the connection from
> the streaming of the stack to the provided global streaming operator.

Do you get compiler error, or linker error? I suspect the latter. You
see, your friend declaration also serves as a declaration for the
non-template operator<<. So now the compiler believes such a
non-template function exists. During overload resolution, the compiler
prefers non-templates over templates. But of course the non-template
version is not implemented anywhere.

> Is there something I'm missing here? Is this no longer valid in C++
> or is this some quirk of VC++ 2005, or is there some new switch I
> need or some such thing?

Your code was not valid, and was compiling on VC2002 by accident. The
compiler bug that allowed the code to compile has been fixed. Now you
need to fix your code.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925



Re: A change from 2002 to 2005 wrt to global templatized operators? by Dean

Dean
Sun Sep 24 16:38:35 CDT 2006

Thanks. I'd just found the answer by stumbling across a similar question by
someone else. The search never works until you first demonstrate your
ignorance in public, then the next search always provides the answer you
were looking for.

-------------------------------------
Dean Roddey
Chairman/CTO, Charmed Quark Systems
www.charmedquark.com

"Igor Tandetnik" <itandetnik@mvps.org> wrote in message
news:%23UvFo7B4GHA.3344@TK2MSFTNGP05.phx.gbl...
> "Dean Roddey" <droddey@charmedquark.com> wrote in message
> news:eSfudpB4GHA.4764@TK2MSFTNGP05.phx.gbl
>> template <class T> class TFundStack : public TObject
>> {
>> public :
>> .......
>>
>> protected :
>> friend TBinOutStream& operator<<
>> (
>> TBinOutStream& strmOut
>> , const TFundStack<T>& fcolToStream
>> );
>
> This befriends a non-template function.
>



Re: A change from 2002 to 2005 wrt to global templatized operators? by Alex

Alex
Sun Sep 24 16:40:15 CDT 2006

"Dean Roddey" <droddey@charmedquark.com> wrote in message
news:eSfudpB4GHA.4764@TK2MSFTNGP05.phx.gbl...
> We just upgraded from a quite old VC++ to 2005. In the
> process, something has stopped working that seems like
> it's completely legal. Basically we have our own very
> large framework. We have the usual streaming classes (base
> classes for in and out streams, from which various
> specific streaming classes are derived for files, memory,
> etc...) and the usual
> templatized collection classes. We want our collections to
> be streamable, where the instantiation element supports
> it, so we provide the usual global (friend) streaming
> operators with each collection type, so that streamability
> isn't forced on the instantiation element types, something
> like:
>
> //
> // A templatized collection class with friend
> declarations for
> // the streaming operators
> //
> template <class T> class TFundStack : public TObject
> {
> public :
> .......
>
> protected :
> friend TBinOutStream& operator<<
> (
> TBinOutStream& strmOut
> , const TFundStack<T>& fcolToStream
> );
>
> friend TBinInStream& operator>>
> (
> TBinInStream& strmIn
> , TFundStack<T>& fcolToStream
> );
> }
>
> // In the same header, global streaming operators
> template <class T> TBinInStream&
> operator>>(TBinInStream& strmToReadFrom, TFundStack<T>&
> colToStream)
> {
> }
>
> template <class T> TBinOutStream&
> operator<<(TBinOutStream& strmToWriteTo, const
> TFundStack<T>& colToStream)
> {
> }
>
> * TBinInStream and TBinOutStream are the base classes from
> which various types of binary in/out streams are derived.
>
> // In a test app, I'd create a stack, put something in it
> TFundStack<tCIDLib::TCard4> fcolStack(4);
> fcolStack.Push(1);
>
> // Then create an memory based output stream and stream
> the stack
> TBinMBufOutStream strmTestOut(8192, 8192);
> strmTest << fcolStack << kCIDLib::FlushIt;
>
>
> The compiler doesn't find the global operators and I get
> an unresolved symbol error on operator<<(TBinOutStream&,
> TFundStack<tCIDLib::TCard4>), so it's not making the
> connection from the streaming of the stack to the provided
> global streaming operator.
>
> Is there something I'm missing here? Is this no longer
> valid in C++ or is this some quirk of VC++ 2005, or is
> there some new switch I need or some such thing?


Whether it's bug or not, but declaring friend operators like
this:

template <class T> // <-- NB!
friend TBinOutStream& operator<<
(
TBinOutStream& strmOut
, const TFundStack<T>& fcolToStream
);

template <class T> // <-- NB!
friend TBinInStream& operator>>
(
TBinInStream& strmIn
, TFundStack<T>& fcolToStream
);

fixes the problem.

I'd interpret this error in favor of compiler, because

a) the declaration of operators <</>> must specify that
it is a template (since unknown type TFundStack<T> is used),
b) the declaration of operators <</>> is inconsistent
with corresponding definition (where template is specified).

HTH
Alex