This one compiles and works like it should (non-nested template):
#include
template class Z;
template
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;
}
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.
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.
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