Search code examples
c++templatesinner-classesfriend

C++: Cannot make operator<< a friend of a templated nested class


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.


Solution

  • 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: