compiler error

A

Aston Martin

can anyone point out why compiler is not happy ? it finds ambiguity in
the friend function declaration and its implementation. if you
eliminate the 'dummy' template parameter altogether, it works. what
effect 'dummy' is having ? it is important to have the function as
friend, since so i can make SomeClass look-n-feel as builtin.

#include<iostream>
using namespace std;

template<typename T, int dummy>
class SomeClass
{
private:
T someVariable;

public:
SomeClass() : someVariable(65) {}
~SomeClass() {}

template<typename T, int dummy>
friend ostream& operator << ( ostream& outStream, const
SomeClass<T, dummy>& sc );
};

template<typename T, int dummy>
ostream& operator << ( ostream& outStream, const SomeClass<T, dummy>&
sc )
{
outStream << "someVariable: " << sc.someVariable << endl;
return outStream;
}

int main(int argc, char* argv[])
{
SomeClass<int,3> cl;
cout << cl;
return 0;
}
 
Z

Zeppe

Aston said:
template<typename T, int dummy>
friend ostream& operator << ( ostream& outStream, const
SomeClass<T, dummy>& sc );

the function is not template. If it was so, anyway, you were hiding the
original templates names with this second template declaration.

the correct declaration of such a friend function is (with forward
references)

#include<iostream>
using namespace std;

template<typename T, int dummy>
class SomeClass;

template<typename T, int dummy>
ostream& operator << ( ostream& outStream, const SomeClass<T, dummy>& sc );

template<typename T, int dummy>
class SomeClass
{
private:
T someVariable;

public:
SomeClass() : someVariable(65) {}
~SomeClass() {}


friend ostream& operator << <> ( ostream& outStream, const
SomeClass<T, dummy>& sc );
};

template<typename T, int dummy>
ostream& operator << ( ostream& outStream, const SomeClass<T, dummy>&
sc )
{
outStream << "someVariable: " << sc.someVariable << endl;
return outStream;
}

int main(int argc, char* argv[])
{
SomeClass<int,3> cl;
cout << cl;
return 0;
}

Regards,

Zeppe
 
A

Aston Martin

the function is not template. If it was so, anyway, you were hiding the
original templates names with this second template declaration.

the correct declaration of such a friend function is (with forward
references)

#include<iostream>
using namespace std;

Hello Zeppe, thanks for your advise, I see your point, but also
disappointed that even this does give exactly the same error

#include<iostream>
using namespace std;

template<typename T, int dummy>
class SomeClass;

template<typename T, int dummy>
ostream& operator << ( ostream& outStream, const SomeClass<T, dummy>&
sc )
{
outStream << "someVariable: " << sc.someVariable << endl;
return outStream;
}

template<typename T, int dummy>
class SomeClass
{
private:
T someVariable;

public:
SomeClass() : someVariable(65) {}
~SomeClass() {}

template<typename T, int dummy>
friend ostream& operator << ( ostream& outStream, const
SomeClass<T, dummy>& sc );
};

int main(int argc, char* argv[])
{
SomeClass<int,3> cl;
cout << cl;
return 0;
}
 
S

Salt_Peter

Hello Zeppe, thanks for your advise, I see your point, but also
disappointed that even this does give exactly the same error

and it should, since you are redeclaring the template parameters.
Perhaps this might explain it better:

int main()
{
int n = 99;
int n = 0; // why is that not allowed?
}
#include<iostream>
using namespace std;

template<typename T, int dummy>
class SomeClass;

template<typename T, int dummy>
ostream& operator << ( ostream& outStream, const SomeClass<T, dummy>&
sc )
{
outStream << "someVariable: " << sc.someVariable << endl;
return outStream;

}

template<typename T, int dummy>
class SomeClass
{
private:
T someVariable;

public:
SomeClass() : someVariable(65) {}
~SomeClass() {}

template<typename T, int dummy>
friend ostream& operator << ( ostream& outStream, const
SomeClass<T, dummy>& sc );

The error tells you what you did wrong. typename T and int dummy are
redeclared.

template<typename U, int Dummy>
friend ostream&
operator << ( ostream& outStream,
const SomeClass<U, Dummy>& sc );

....or...

friend ostream&
operator << < > ( ostream& outStream,
const SomeClass said:
};

int main(int argc, char* argv[])
{
SomeClass<int,3> cl;
cout << cl;
return 0;

}
 
Z

Zeppe

Aston said:
> Hello Zeppe, thanks for your advise, I see your point, but also
> disappointed that even this does give exactly the same error
>
> #include<iostream>
> using namespace std;
>
> template<typename T, int dummy>
> class SomeClass;
>
> template<typename T, int dummy>
> ostream& operator << ( ostream& outStream, const SomeClass<T, dummy>&
> sc )
> {
> outStream << "someVariable: " << sc.someVariable << endl;
> return outStream;
> }
>
> template<typename T, int dummy>
> class SomeClass
> {
> private:
> T someVariable;
>
> public:
> SomeClass() : someVariable(65) {}
> ~SomeClass() {}
>
> template<typename T, int dummy>
> friend ostream& operator << ( ostream& outStream, const
> SomeClass<T, dummy>& sc );

you are declaring again your function as a template, while it is not. I
mean, the function is a template on the parameters of SomeClass, but you
don't want to declare the general template function as a friend of
SomeClass<T,dummy>. You want to declare only the function operator<<
<T,dummy>(...) as a friend of SomeClass<T,dummy>. That's way you have to
remove the template from the friend declaration:


friend ostream& operator << <> ( ostream& outStream, const
SomeClass<T, dummy>& sc );

If you wanted to make the generic template class as friend, you have to
change the names of the templates:

template<typename T1, int dummy1>
friend ostream& operator << ( ostream& outStream, const
SomeClass<T1, dummy1>& sc );

but there is no point to make the generic template function as a friend.
It's always good to reduce the scope of the friend as much as possible,
because friend is evil, in general.


Regards,

Zeppe
 
A

Aston Martin

but there is no point to make the generic template function as a friend.
It's always good to reduce the scope of the friend as much as possible,
because friend is evil, in general.

Regards,

Zeppe

Salt_Peter, Zeppe

I understand what you said. It was re-declaration and function is
expected to 're-use' class template parameters. I had guessed so and
tried version below, which works. I just wanted to know that how to
give body(provide definition) to

friend ostream& operator << ( ostream& outStream, const SomeClass<T,
dummy>& sc );

declared in SomeClass<T,dummy>, outside class(see commented code).
linker complains if you declare in class and provide implementation
outside. or is it the only possible way to go.

#include<iostream>
using namespace std;

template<typename T, int dummy>
class SomeClass
{
private:
T someVariable;

public:
SomeClass() : someVariable(65) {}
~SomeClass() {}

friend ostream& operator << ( ostream& outStream, const
SomeClass<T, dummy>& sc )
{
outStream << sc.someVariable;
return outStream;
}
};

int main(int argc, char* argv[])
{
SomeClass<int,3> cl;
cout << cl;
return 0;
}

//template<typename T, int dummy>
//ostream& operator << ( ostream& outStream, const SomeClass<T,
dummy>& sc )
//{
// outStream << "someVariable: " << sc.someVariable << endl;
// return outStream;
//}
 
S

Salt_Peter

Salt_Peter, Zeppe

I understand what you said. It was re-declaration and function is
expected to 're-use' class template parameters. I had guessed so and
tried version below, which works. I just wanted to know that how to
give body(provide definition) to

friend ostream& operator << ( ostream& outStream, const SomeClass<T,
dummy>& sc );

declared in SomeClass<T,dummy>, outside class(see commented code).
linker complains if you declare in class and provide implementation
outside. or is it the only possible way to go.

The linker complains because your declaration is wrong. Do you see the
'< >' to indicate that the operator is indeed a template
specialization of Someclass<T,dummy>? Look harder.

template<typename T, int dummy>
class SomeClass
{
...
friend ostream&
operator<< < > ( ostream&, const SomeClass<T, dummy>& );
};

template<typename T, int dummy>
ostream& operator << ( ostream& outStream,
const SomeClass<T, dummy>& sc )
{
outStream << "someVariable: " << sc.someVariable << endl;
return outStream;

}

And Zeppe is right when he says:
You want to declare only the function operator<< <T,dummy>(...) as a
friend of SomeClass<T,dummy>. Thats why you need ... operator<< < >
(...);
 
J

James Kanze

Aston Martin wrote:
you are declaring again your function as a template, while it is not. I
mean, the function is a template on the parameters of SomeClass, but you
don't want to declare the general template function as a friend of
SomeClass<T,dummy>. You want to declare only the function operator<<
<T,dummy>(...) as a friend of SomeClass<T,dummy>. That's way you have to
remove the template from the friend declaration:
friend ostream& operator << <> ( ostream& outStream, const
SomeClass<T, dummy>& sc );

Does this work with all compilers today? (And does it declare
all template instantiations as friend, or just the relevant
one?)

My usual solution is:

template< typename T, int dummy >
class SomeClass
{
std::eek:stream& print( std::eek:stream& dest ) const ;
// member function, no problem...
friend std::eek:stream& operator<<( std::eek:stream& dest,
SomeClass const& obj )
{
return obj.print( dest ) ;
}
// ...
} ;

By putting the implementation of the (non template) function
inline, I get a new version of it---the one I need---every time
I instantiate the class.
 
Z

Zeppe

James said:
Does this work with all compilers today? (And does it declare
all template instantiations as friend, or just the relevant
one?)

well, I don't know if it does work with all the compilers. It works on
vc8, g++ 4.1 and g++ 3.4 for sure, because I tried it. It declares only
the relevant function as friend.
My usual solution is:

template< typename T, int dummy >
class SomeClass
{
std::eek:stream& print( std::eek:stream& dest ) const ;
// member function, no problem...
friend std::eek:stream& operator<<( std::eek:stream& dest,
SomeClass const& obj )
{
return obj.print( dest ) ;
}
// ...

In my opinion your solution is much more elegant. You can also avoid
friend if you put your print public, given that is a safe function (even
if it complicates without reason the public interface of the class).
Usually in my case, when the classes haven't got the getters for all the
parameters that I want to show (that is not so common), I go for a
solution similar to yours, and if the performances are not a problem and
it makes sense, I create a 'std::string toString()' public method
without friend that will be called by operator<<.

Regards,

Zeppe
 
A

Aston Martin

And Zeppe is right when he says:
You want to declare only the function operator<< <T,dummy>(...) as a
friend of SomeClass<T,dummy>. Thats why you need ... operator<< < >
(...);

Salt_peter,

thanks again. I learnt something. but did u build before posting ?
since for me linker still fails linkage!!
yeah, I looked harder and found "< >' :) and declared the function as
below

friend ostream& operator << < >( ostream& outStream, const
SomeClass<T, dummy>& sc );

~Aston
 
S

Salt_Peter

Salt_peter,

thanks again. I learnt something. but did u build before posting ?
since for me linker still fails linkage!!
yeah, I looked harder and found "< >' :) and declared the function as
below

friend ostream& operator << < >( ostream& outStream, const
SomeClass<T, dummy>& sc );

~Aston


Yes, builds fine here (g++ 4.1.1). Some compilers do balk at that
though.
If yours does, use the generic template or leave it inline.
 
A

Aston Martin

Yes, builds fine here (g++ 4.1.1). Some compilers do balk at that
though.
If yours does, use the generic template or leave it inline.

hmm, inline is fine for me though.
thanks Zeppe and Salt_Peter for your help and expert comments.

Aston
 
J

James Kanze

well, I don't know if it does work with all the compilers. It works on
vc8, g++ 4.1 and g++ 3.4 for sure, because I tried it. It declares only
the relevant function as friend.

The reason I ask is that all of this is related to how friend
declarations are handled, and the C++ standard changed this with
regards to what was generally being done before. Which means
that I would have some doubts if you had to support older
compilers. (Both g++ 4.1 and VC++ 8 are very up to date, and
g++ 3.4 is fairly up to date in this respect as well. Where I
work, however, I have to support older versions of Sun CC, and
the Windows programmers still use VC++ 6. Regretfully, I fear
that this situation is rather widespread.)
In my opinion your solution is much more elegant. You can also avoid
friend if you put your print public, given that is a safe function (even
if it complicates without reason the public interface of the class).

No you can't:). My solution requires that the operator be
defined inline, in the class body, and the only way you can
define a non member function in a class body is to declare it
friend. In this case, friendship isn't about giving access
rights.

In practice, I have a class template, IOStreamOps, which looks
about like:

template< typename T >
struct IOStreamOps
{
friend std::eek:stream&operator<<( std::eek:stream& dest, T const&
source )
{
source.print( dest ) ;
return dest ;
}
friend std::istream&operator>>( std::istream& source, T&
dest )
{
dest.scan( source ) ;
return source ;
}
} ;

If I want to provide IO in a class, I'll derive from the
appropriate instantiation, and define a print (and/or scan)
function in the class, e.g.:

class MyClass : public IOStreamOps< MyClass >
{
public:
// ...
void print( std::eek:stream& ) ;
} ;

(I actually started doing this as a means of getting all of the
binary operators: given +=, -=, etc., deriving from
ArithmeticOps<> gives my +, -, etc. I believe that the idea
originally comes from Barton and Nackman, but it was Michel
Michaud who suggested it to me.)

Note that this works both with older compilers, and with newer
ones, but for different reasons. In traditional, pre-standard
C++, a function declared as a friend was "injected" into the
surrounding namespace (except that in traditional C++, it wasn't
"namespace", but file scope), and so would be found by normal
lookup. The C++ standard suppressed this injection, but ADL
will look inside the relevant classes, and finds the name.

Also, I'm not sure what you'd call a function like this.
Formally, it's not a function template, but a normal, free
function. But it gets instantiated exactly like a template
function; in fact, with regards to templates, it behaves exactly
like a (static) member function of the class template. Except,
of course, that it isn't a member.
Usually in my case, when the classes haven't got the getters for all the
parameters that I want to show (that is not so common), I go for a
solution similar to yours, and if the performances are not a problem and
it makes sense, I create a 'std::string toString()' public method
without friend that will be called by operator<<.

Interesting. I generally do the opposite: the actual conversion
is in the print() function, and the toString() function uses
std::eek:stringstream to generate the string, e.g.:

std::string
MyClass::asString() const
{
std::eek:stringstream result ;
print( result ) ;
return result.str() ;
}

Practically speaking, as soon as the class contains any numeric
types, or containers, or any user defined class that supports
<< (but not asString()), I need the ostringstream anyway. So I
might as well use it up front (and be sure of only using one).
 
Z

Zeppe

James said:
No you can't:). My solution requires that the operator be
defined inline, in the class body, and the only way you can
define a non member function in a class body is to declare it
friend. In this case, friendship isn't about giving access
rights.

Oh gosh, I haven't thought about that aspect :) I see now!
In practice, I have a class template, IOStreamOps, which looks
about like: [SNIP]
(I actually started doing this as a means of getting all of the
binary operators: given +=, -=, etc., deriving from
ArithmeticOps<> gives my +, -, etc. I believe that the idea
originally comes from Barton and Nackman, but it was Michel
Michaud who suggested it to me.)

Well... it's a great approach! It encapsulates the friend declaration
that is great, and it forces you to have a uniform interface for the
input outup methods scan and print. I like it.
Note that this works both with older compilers, and with newer
ones, but for different reasons. In traditional, pre-standard
C++, a function declared as a friend was "injected" into the
surrounding namespace (except that in traditional C++, it wasn't
"namespace", but file scope), and so would be found by normal
lookup. The C++ standard suppressed this injection, but ADL
will look inside the relevant classes, and finds the name.

Nice to know. I've started programming at a "professional" level just
one and an half year ago, so I've relatively little experience with the
older compilers.
Interesting. I generally do the opposite: the actual conversion
is in the print() function, and the toString() function uses
std::eek:stringstream to generate the string, e.g.:

std::string
MyClass::asString() const
{
std::eek:stringstream result ;
print( result ) ;
return result.str() ;
}

Practically speaking, as soon as the class contains any numeric
types, or containers, or any user defined class that supports
<< (but not asString()), I need the ostringstream anyway. So I
might as well use it up front (and be sure of only using one).

Fair enough. You can also add the asString method in the IOStreamOps
interface (maybe slightly changing the name), in order to provide the
support for a quick output to string as well. The alternative way, which
could be an helper method

template <class T>
std::string ToString(const T& obj)
{
std::eek:stringstream ost;
ost << obj;
return ost.str();
}

has got the advantage that can be used for objects that do not derive
from the IOStreamOps, as well (even if it's a bit of a functional
approach). The correspondent input function:

template <class T>
T FromString(const std::string& str)
{
std::istringstream ist(str);
T t;
str >> t;
return t;
}

has got a lot more drawbacks (need for the empty T constructor, the copy
constructor of T called when returning the object, the need to
explicitly specify the template parameters, and finally the object data
are not removed from the generating string), but it can be useful anyway
from time to time.

Regards,

Zeppe
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top