Friend template operator - c++

The following program does not compile in MS Visual Studio 19.
#include <iostream>
#include <string>
template <typename T>
class A;
template <typename T>
std::ostream &operator <<( std::ostream &, const A<T> & );
template <typename T>
class A
{
private:
T x;
public:
A( const T &x ) : x( x ) {}
friend std::ostream &::operator <<( std::ostream &, const A<T> & );
};
template <typename T>
std::ostream &operator <<( std::ostream &os, const A<T> &a )
{
return os << "a.x = " << a.x;
}
int main()
{
std::cout << A<std::string>( "Hello" ) << '\n';
}
The compiler says that operator << is not a function.
While the following program
#include <iostream>
#include <string>
template <typename T>
class A;
template <typename T>
std::ostream &f( std::ostream &, const A<T> & );
template <typename T>
class A
{
private:
T x;
public:
A( const T &x ) : x( x ) {}
friend std::ostream &::f( std::ostream &, const A<T> & );
};
template <typename T>
std::ostream &f( std::ostream &os, const A<T> &a )
{
return os << "a.x = " << a.x;
}
int main()
{
f( std::cout, A<std::string>( "Hello" ) ) << '\n';
}
compiles successfully.
What is the reason of that the first program does not compile? Is it a bug of MS Visual Studio 19 or do I have missed something from the C++ 20 Standard?
According to the C++ 20 Standard (13.7.4 Friends)
(1.3) — if the name of the friend is a qualified-id and a matching
function template is found in the specified class or namespace, the
friend declaration refers to the deduced specialization of that
function template (13.10.2.6), otherwise,

Your program is defective; however there is indeed a bug (or two) in Visual Studio.
First, your friend declaration should be spelled with a <> to indicate that it is a template specialization that is the friend:
friend std::ostream &::operator <<<>( std::ostream &, const A<T> & );
// ^~
It is also acceptable to spell it with T explicitly (i.e. not inferring the template arguments):
friend std::ostream &::operator <<<T>( std::ostream &, const A<T> & );
// ^~~
Incidentally, gcc provides a helpful hint to make this change.
However MSVC will still erroneously reject the program; this appears to only happen templates of the specific operator<< where the class template argument is passed by reference, but not by value:
#include <iostream>
template<class> struct A;
template<class> struct B;
template<class T> std::ostream& f(std::ostream&, A<T>);
template<class T> std::ostream &operator<<(std::ostream&, A<T>);
template<class T> std::ostream& f(std::ostream&, B<T> const&);
template<class T> std::ostream &operator<<(std::ostream&, B<T> const&);
template<class T> struct A {
friend std::ostream& ::f<T>(std::ostream&, A<T>);
friend std::ostream& ::operator<<<T>(std::ostream&, A<T>);
};
template<class T> struct B {
friend std::ostream& ::f<T>(std::ostream&, B<T> const&);
friend std::ostream& ::operator<<<T>(std::ostream&, B<T> const&); // MSVC bug
};
template<class T> std::ostream& f(std::ostream& os, A<T>) { return os << "1"; }
template<class T> std::ostream& operator<<(std::ostream& os, A<T>) { return os << "2"; }
template<class T> std::ostream& f(std::ostream& os, B<T> const&) { return os << "3"; }
template<class T> std::ostream& operator<<(std::ostream& os, B<T> const&) { return os << "4"; }
int main() {
f(std::cout, A<int>()) << ' ' << A<int>() << '\n';
f(std::cout, B<int>()) << ' ' << B<int>() << '\n';
}
The fact that MSVC rejects only friend std::ostream& ::operator<<<T>(std::ostream&, B<T> const&) makes it clear that this is a compiler bug. gcc and clang accept and print 1 2\n3 4, as expected.
Another, similar defect in MSVC is that it will reject friend std::ostream& ::f<>(std::ostream&, A) (that is, using the injected-class-name to name A<T>).
Regarding the Standard, the language was changed substantially post-C++20 by P1787R6: Declarations and where to find them; in particular, it removes the language that you quote in your question. As such, I don't think you can use that as an argument for being allowed to omit the <> from the friend function template specialization declaration, since P1787R6 resolves a bunch of CWG DRs and so can be regarded as a post-publication correction.

Related

friend operator<< of a template class, with additional default template parameter

In the following code, A is a template class, depending on a non-type bool type parameter. A friend operator<< is defined for both A<true> and A<false>. The operator<< additionally depends on another bool template parameter.
#include <ostream>
#include <iostream>
#include <type_traits>
template <bool type>
class A;
template <>
class A<true> {
int m_x;
public:
A(int x) : m_x{x} { }
template <bool d, bool type>
friend std::ostream& operator<<(std::ostream& os, const A<type>& a);
};
template <>
class A<false> {
int m_y;
public:
A(int y) : m_y{y} { }
template <bool d, bool type>
friend std::ostream& operator<<(std::ostream& os, const A<type>& a);
};
template <bool d, bool type>
std::ostream& operator<<(std::ostream& os, const A<type>& a)
{
if constexpr (type) {
os << "m_x = " << a.m_x << std::endl;
if constexpr (d) { os << "2m_x = " << a.m_x << std::endl; }
}
else {
os << "m_y = " << a.m_y << std::endl;
if constexpr (d) { os << "2m_y = " << a.m_y << std::endl; }
}
return os;
}
int main()
{
A<true> atrue{2};
A<false> afalse{3};
operator<< <true>(std::cout, atrue);
operator<< <false>(std::cout, atrue);
operator<< <true>(std::cout, afalse);
operator<< <false>(std::cout, afalse);
return 0;
}
See it live on Coliru.
Now, I would like to give a default value of the template parameter d of operator<<, say d=false such that this statement
std::cout << atrue;
is equivalent to
operator<< <false>(std::cout, atrue);
because bool d takes a default value d=false and bool type is deduced from the second argument of operator<<.
Is there a syntax to allow for that?
If I insert the default parameter in the friend declaration
template <bool d = false, bool type>
friend std::ostream& operator<<(std::ostream& os, const A<type>& a);
I get a compile error:
main.cpp:14:71: error: default template arguments may not be used in
template friend declarations
If I insert the default parameter in the code of operator<<
template <bool d = false, bool type>
std::ostream& operator<<(std::ostream& os, const A<type>& a)
{
...
again it does not compile giving error
main.cpp:27:15: error: redeclaration of friend 'template std::ostream& operator<<(std::ostream&, const A&)' may not
have default template arguments
27 | std::ostream& operator<<(std::ostream& os, const A& a)
main.cpp:14:26: note: 'template std::ostream&
operator<<(std::ostream&, const A&)' previously declared here
14 | friend std::ostream& operator<<(std::ostream& os, const
A& a);
OK, actually the solution was relatively easy. It is enough to add a declaration of the template operator<< before the class specializations:
template <bool type>
class A;
template <bool d = false, bool type>
std::ostream& operator<<(std::ostream& os, const A<type>& a);
....
In this way the friend declaration inside A<type> does not declare operator<< in first place, but only declares that it is a friend.
A working example can be checked here.

c++ template class operator<< overloading [duplicate]

I have read couple of the questions regarding my problem on StackOverflow.com now, and none of it seems to solve my problem. Or I maybe have done it wrong...
The overloaded << works if I make it into an inline function. But how do I make it work in my case?
warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function
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
/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)'
collect2: ld returned 1 exit status
The code:
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const;
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
template <class classT>
ostream& operator<<(ostream &os, const D<classT>& rhs)
{
os << rhs.d;
return os;
}
This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.
The extrovert
Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.
template <typename T>
class Test {
template <typename U> // all instantiations of this template are my friends
friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
// Can access all Test<int>, Test<double>... regardless of what T is
}
The introverts
Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).
This can be done in two ways, the simple way being as #Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:
template <typename T>
class Test {
friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
// can access the enclosing Test. If T is int, it cannot access Test<double>
}
};
In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.
The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:
// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );
// Declare the actual templates:
template <typename T>
class Test {
friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
// Can only access Test<T> for the same T as is instantiating, that is:
// if T is int, this template cannot access Test<double>, Test<char> ...
}
Taking advantage of the extrovert
The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:
namespace hacker {
struct unique {}; // Create a new unique type to avoid breaking ODR
template <>
std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
{
// if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
// if Test<T> is an introvert, then I can only mess up with Test<unique>
// which is just not so much fun...
}
}
You can't declare a friend like that, you need to specify a different template type for it.
template <typename SclassT>
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);
note SclassT so that it doesn't shadow classT. When defining
template <typename SclassT>
ostream& operator<< (ostream & os, const D<SclassT>& rhs)
{
// body..
}
This worked for me without any compiler warnings.
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const {
return (d > rhs.d);
}
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D& rhs) {
os << rhs.d;
return os;
}
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
I think you shouldn't make friend in the first place.
You can create a public method call print, something like this (for a non template class):
std::ostream& MyClass::print(std::ostream& os) const
{
os << "Private One" << privateOne_ << endl;
os << "Private Two" << privateTwo_ << endl;
os.flush();
return os;
}
and then, outside the class (but in the same namespace)
std::ostream& operator<<(std::ostream& os, const MyClass& myClass)
{
return myClass.print(os);
}
I think it should work also for template class, but I haven't tested yet.
Here you go:
#include <cstdlib>
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const { return d > rhs.d;};
classT operator=(const D<classT>& rhs);
template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs)
{
os << rhs.d;
return os;
}
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}

What is the right whay to overload the ostream operator, when using templates? [duplicate]

I have read couple of the questions regarding my problem on StackOverflow.com now, and none of it seems to solve my problem. Or I maybe have done it wrong...
The overloaded << works if I make it into an inline function. But how do I make it work in my case?
warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function
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
/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)'
collect2: ld returned 1 exit status
The code:
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const;
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
template <class classT>
ostream& operator<<(ostream &os, const D<classT>& rhs)
{
os << rhs.d;
return os;
}
This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.
The extrovert
Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.
template <typename T>
class Test {
template <typename U> // all instantiations of this template are my friends
friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
// Can access all Test<int>, Test<double>... regardless of what T is
}
The introverts
Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).
This can be done in two ways, the simple way being as #Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:
template <typename T>
class Test {
friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
// can access the enclosing Test. If T is int, it cannot access Test<double>
}
};
In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.
The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:
// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );
// Declare the actual templates:
template <typename T>
class Test {
friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
// Can only access Test<T> for the same T as is instantiating, that is:
// if T is int, this template cannot access Test<double>, Test<char> ...
}
Taking advantage of the extrovert
The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:
namespace hacker {
struct unique {}; // Create a new unique type to avoid breaking ODR
template <>
std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
{
// if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
// if Test<T> is an introvert, then I can only mess up with Test<unique>
// which is just not so much fun...
}
}
You can't declare a friend like that, you need to specify a different template type for it.
template <typename SclassT>
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);
note SclassT so that it doesn't shadow classT. When defining
template <typename SclassT>
ostream& operator<< (ostream & os, const D<SclassT>& rhs)
{
// body..
}
This worked for me without any compiler warnings.
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const {
return (d > rhs.d);
}
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D& rhs) {
os << rhs.d;
return os;
}
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
I think you shouldn't make friend in the first place.
You can create a public method call print, something like this (for a non template class):
std::ostream& MyClass::print(std::ostream& os) const
{
os << "Private One" << privateOne_ << endl;
os << "Private Two" << privateTwo_ << endl;
os.flush();
return os;
}
and then, outside the class (but in the same namespace)
std::ostream& operator<<(std::ostream& os, const MyClass& myClass)
{
return myClass.print(os);
}
I think it should work also for template class, but I haven't tested yet.
Here you go:
#include <cstdlib>
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const { return d > rhs.d;};
classT operator=(const D<classT>& rhs);
template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs)
{
os << rhs.d;
return os;
}
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}

operator<<(ostream&, X) for class X nested in a class template

This one compiles and works like it should (non-nested template):
#include <iostream>
template<typename T> class Z;
template <typename T>
std::ostream& operator<< (std::ostream& os, const Z<T>&) {
return (os << "Z");
}
template<typename T> class Z {
friend std::ostream& operator<< <> (std::ostream& os, const Z&);
};
int main () {
Z<int> z;
std::cout << z << std::endl;
}
This one doesn't compile (gcc 4.4 and gcc 4.6, in both 03 and 0x mode):
#include <iostream>
template<typename T> class Z;
template<typename T>
std::ostream& operator<< (std::ostream& os, const typename Z<T>::ZZ&) {
return (os << "ZZ!");
}
template <typename T> class Z {
public:
class ZZ {
friend std::ostream& operator<< <> (std::ostream& os, const ZZ&);
};
};
int main () {
Z<int>::ZZ zz;
std::cout << zz << std::endl;
}
The error message looks like this:
error: template-id ‘operator<< <>’ for ‘std::ostream& operator<<(std::ostream&,
const Z<int>::ZZ&)’ does not match any template declaration
error: no match for ‘operator<<’ in ‘std::cout << zz’
In the 0x mode the second error message is different, but the meaning is the same.
Is it possible to do what I want to do?
EDIT Apparently, there's an instance of non-deduced context here, which explains the error messages. The question, however, still stands: can I have a working operator<< for a class nested in a class template?
This is a general problem for functions:
template <typename C>
void func(typename C::iterator i);
Now, if I call func(int*), which value of C should I use ?
In general, you cannot work backward ! Many different C could have defined an internal type iterator that happens to be a int* for some set of parameters.
In your case, you are complicating the situation a bit:
template <typename T>
void func(typename Z<T>::ZZ const&);
But fundamentally this is the same issue, Z<T> is a template, not a full class, and you are asking to create a function for the inner type ZZ of this template.
Suppose I do:
template <typename T>
struct Z { typedef T ZZ; };
template <typename T>
struct Z<T const> { typedef T ZZ; };
Note: typical of iterators, the value_type is not const-qualified
Then, when invoking func(int), should I use Z<int> or Z<int const> ?
It is non-deducible.
And thus the whole thing is referred to as a non-deducible context, and the Standard forbids it because there is no sensible answer.
Rule of Thumb: be suspicious of typename in the parameters of a function.
Note: they are OK if another argument already pinned down the type, example typename C::iterator find(C&, typename C::const_reference); because once C is deduced, then C::const_reference may be used without trouble
Apart from the problem that the friend declaration doesn't match the operator template (perhaps fixable as)
class ZZ {
template<class U>
friend std::ostream& operator<<(std::ostream& os, const ZZ&);
};
you also have a problem with a "non-deduced context", which is what Matthieu links to.
In this template
template<typename T>
std::ostream& operator<< (std::ostream& os, const typename Z<T>::ZZ&) {
return (os << "ZZ!");
}
the compiler isn't able to figure out for what T's you parameter will match. There could be several matches, if you specialize for some types
template<>
class Z<long>
{
public:
typedef double ZZ;
};
template<>
class Z<bool>
{
public:
typedef double ZZ;
};
Now if I try to print a double, T could be either bool or long.
The compiler cannot know this for sure without checking for all possible T's, and it doesn't have to do that. It just skips your operator instead.
Matthieu explained the problem very well, but a simple work-around can be used in this case.
You can implement the friend function inside the class with the friend declaration:
#include <iostream>
template <typename T> class Z {
public:
class ZZ {
friend std::ostream& operator<< (std::ostream& os, const ZZ&) {
return os << "ZZ!";
}
};
};
int main () {
Z<int>::ZZ zz;
std::cout << zz << std::endl;
}
A different technique is to provide an out of line class template which works like a hook.
template <typename T> class ZZZ {
T &getDerived() { return static_cast<T&>(*this); }
T const &getDerived() const { return static_cast<T const&>(*this); }
protected:
~ZZZ() { }
};
template <typename T> class Z {
public:
class ZZ : public ZZZ<ZZ> {
};
};
template<typename ZZ>
std::ostream& operator<< (std::ostream& os, const ZZZ<ZZ> &zzz) {
ZZ const& zz = zzz.getDerived();
/* now you can use zz */
return (os << "ZZ!");
}
Using a friend function definition is preferable though. But it's good to know about alternatives.

overloading friend operator<< for template class

I have read couple of the questions regarding my problem on StackOverflow.com now, and none of it seems to solve my problem. Or I maybe have done it wrong...
The overloaded << works if I make it into an inline function. But how do I make it work in my case?
warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function
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
/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)'
collect2: ld returned 1 exit status
The code:
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const;
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
template <class classT>
ostream& operator<<(ostream &os, const D<classT>& rhs)
{
os << rhs.d;
return os;
}
This is one of those frequently asked questions that have different approaches that are similar but not really the same. The three approaches differ in who you are declaring to be a friend of your function --and then on how you implement it.
The extrovert
Declare all instantiations of the template as friends. This is what you have accepted as answer, and also what most of the other answers propose. In this approach you are needlessly opening your particular instantiation D<T> by declaring friends all operator<< instantiations. That is, std::ostream& operator<<( std::ostream &, const D<int>& ) has access to all internals of D<double>.
template <typename T>
class Test {
template <typename U> // all instantiations of this template are my friends
friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
// Can access all Test<int>, Test<double>... regardless of what T is
}
The introverts
Only declare a particular instantiation of the insertion operator as a friend. D<int> may like the insertion operator when applied to itself, but it does not want anything to do with std::ostream& operator<<( std::ostream&, const D<double>& ).
This can be done in two ways, the simple way being as #Emery Berger proposed, which is inlining the operator --which is also a good idea for other reasons:
template <typename T>
class Test {
friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
// can access the enclosing Test. If T is int, it cannot access Test<double>
}
};
In this first version, you are not creating a templated operator<<, but rather a non-templated function for each instantiation of the Test template. Again, the difference is subtle but this is basically equivalent to manually adding: std::ostream& operator<<( std::ostream&, const Test<int>& ) when you instantiate Test<int>, and another similar overload when you instantiate Test with double, or with any other type.
The third version is more cumbersome. Without inlining the code, and with the use of a template, you can declare a single instantiation of the template a friend of your class, without opening yourself to all other instantiations:
// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );
// Declare the actual templates:
template <typename T>
class Test {
friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
// Can only access Test<T> for the same T as is instantiating, that is:
// if T is int, this template cannot access Test<double>, Test<char> ...
}
Taking advantage of the extrovert
The subtle difference between this third option and the first is in how much you are opening to other classes. An example of abuse in the extrovert version would be someone that wants to get access into your internals and does this:
namespace hacker {
struct unique {}; // Create a new unique type to avoid breaking ODR
template <>
std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
{
// if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
// if Test<T> is an introvert, then I can only mess up with Test<unique>
// which is just not so much fun...
}
}
You can't declare a friend like that, you need to specify a different template type for it.
template <typename SclassT>
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);
note SclassT so that it doesn't shadow classT. When defining
template <typename SclassT>
ostream& operator<< (ostream & os, const D<SclassT>& rhs)
{
// body..
}
This worked for me without any compiler warnings.
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const {
return (d > rhs.d);
}
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D& rhs) {
os << rhs.d;
return os;
}
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
I think you shouldn't make friend in the first place.
You can create a public method call print, something like this (for a non template class):
std::ostream& MyClass::print(std::ostream& os) const
{
os << "Private One" << privateOne_ << endl;
os << "Private Two" << privateTwo_ << endl;
os.flush();
return os;
}
and then, outside the class (but in the same namespace)
std::ostream& operator<<(std::ostream& os, const MyClass& myClass)
{
return myClass.print(os);
}
I think it should work also for template class, but I haven't tested yet.
Here you go:
#include <cstdlib>
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const { return d > rhs.d;};
classT operator=(const D<classT>& rhs);
template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs)
{
os << rhs.d;
return os;
}
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}