15

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?

Serge
  • 7,706
  • 5
  • 40
  • 46
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • I'm working on it... The first error you get rid off by putting "operator << ", which also makes things a lot more readable. – AudioDroid Jul 04 '11 at 07:56
  • possible duplicate of [How to deduce class type from method type in C++ templates?](http://stackoverflow.com/questions/3830491/how-to-deduce-class-type-from-method-type-in-c-templates) – Matthieu M. Jul 04 '11 at 08:07
  • @Matthieu: I would be interested to see the code from that link adapted to this problem. Could put that as an answer for the "ordinary mortals" like me (and possibly "n.m.") out here? ;-) – AudioDroid Jul 04 '11 at 08:23
  • 1
    You can always implement it right with the friend declaration. (Yes, I assume you expect a better answer, but I'd be damned to know what needs to be done) – Shiroko Jul 04 '11 at 08:52
  • 1
    the same problem has been already discussed here http://stackoverflow.com/questions/4092237/c-nested-class-of-a-template-class – Serge Jul 04 '11 at 09:26
  • @AudioDroid: I did :) It really is an arcane corner of the template usage :/ – Matthieu M. Jul 04 '11 at 09:38
  • @Shiroko: perhaps yours is the most sensible solution. If you post it as an answer I'll accept it. @Serge: the solution you're linking also works though it's rather hairy. – n. m. could be an AI Jul 04 '11 at 11:33

4 Answers4

7

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

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    So is there any workaround? I want `operator<<` for my nested class, is there a way to define it? Except defining it inline straight in the friend declaration. – n. m. could be an AI Jul 04 '11 at 15:12
  • 1
    @n.m: no, there is not, as long as the class remains nested. An option would be to define the class outside of the template `Z` (in a `details` namespace for example) alongside its `operator<<` and then within `Z` use a `typedef`. – Matthieu M. Jul 04 '11 at 16:27
  • @Matthieu an option would be to rely on CRTP. See my answer for an explanation. It's not as good as the friend way, but if one can't use it, one could try that :) – Johannes Schaub - litb Jul 05 '11 at 15:09
4

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.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Is there perhaps a way to work around this issue? I can define `operator<<` inline right in the friend declaration, and everything seems to work. But if I don't like an inline definition for some reason, is there any other way? – n. m. could be an AI Jul 04 '11 at 15:14
  • @n.m. - I don't think there is a better way. If you define the operator inside each class, you get around the problem of maching any possible specializations. Templates are very header-only with inline functions, whether we like it or not. :-( – Bo Persson Jul 04 '11 at 16:02
3

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;
}
Shiroko
  • 1,437
  • 8
  • 12
2

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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212