2

This is the basic code structure (I'll reveal more if it becomes relevant to the problem):

class Outer {
    // Forward declaration for the next forward declaration
    template <class C> class Inner;

    // Forward declaration for the actual Inner class definition
    template <class C>
    friend std::ostream& operator<<(std::ostream &os, const Inner<C> &obj);

    template <class C>
    class Inner {
        friend std::ostream& operator<< <>(std::ostream &os, const Inner &obj);
    };
};

Since the inner class is templated, I realized my operator<< override had to be as well. Additionally, the second friend declaration is a template instantiation, so that operator<< <C> is only friends with Outer::Inner<C>.

When I define the operator override in the separate implementation file, it looks like this:

template <class C>
std::ostream& operator<<(std::ostream &os, const Outer::Inner<C> &obj) {
    // definition
}

Over the past several days, I have repeatedly refactored my code to a ridiculous degree in the hopes of making this work. Most recently, I tried only compiling *.h on g++ and received three error messages all for the same line (the one with the template instantiation).

outer.h:x:y1: error: declaration of ‘operator<<’ as non-function
    friend std::ostream& operator<< <>(std::ostream &os, const Inner &obj);
                                 ^~
outer.h:x:y1: error: expected ‘;’ at end of member declaration
outer.h:x:y2: error: expected unqualified-id before ‘<’ token
    friend std::ostream& operator<< <>(std::ostream &os, const Inner &obj);
                                    ^

Despite extensively searching the phrases given in the error output, however, I'm still no closer to solving this, so I'd be grateful if someone with C++ experience, who unlike me knows what they're doing, could please help.

G.S.
  • 63
  • 5
  • 2
    Drop `` in the definition - it's not a valid syntax. Further, templates must be defined in a header file; the compiler needs to see the definition at the point where the template is used. – Igor Tandetnik Aug 15 '19 at 04:03
  • Oh, interesting. I remember that when writing the definition for the inner class, I had to use `template Outer::Inner::Inner() {/*...*/}` for the constructor, with similar lines for the member functions, so I must have gotten confused (should I remove `template ` too?). Besides, the issues I saw just from compiling the header file would still remain. – G.S. Aug 15 '19 at 04:09
  • Possible duplicate of [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – L. F. Aug 15 '19 at 07:08

1 Answers1

0

After much more research, I am convinced that this specific case is actually impossible—an operator overload generally can't be a friend of a class that is both nested and templated. (If you think that's wrong, please let me know why, since I'm far from being a C++ expert myself.)

I've tried many, many different solutions to this, and while I don't think it would be very productive to write a detailed account of every method I tried and the specific reasons for their failures, some of the errors included

  • invalid use of incomplete type class Outer
  • field... has incomplete type Outer::Inner<[Type]>
  • no match for operator<<
  • ambiguous overload for operator<<

in addition to the ones in the original post.

So in this area of my code, I'm ditching the operator<< approach. My less elegant but immediately effective alternative:

class Outer {
    template <class C>
    class Inner {
    public:
        std::ostream& printout(std::ostream &os) {
            os << /* whatever */ << /* I want */ << /* to print */;
            return os;
        }
    };
};

Some potentially helpful links:

G.S.
  • 63
  • 5