Search code examples
c++templatesstlpriority-queuefriend

Not declared in scope - friend comparator class for priority_queue C++


First let me condense the relevant code, note the parameter movies which produces the error:

// ActorGraph.hpp
class ActorGraph
{
protected:
    class Movie; // definition further below
    friend class MovieYearComparator;

    int func(..., priority_queue<Movie, vector<Movie>, MovieYearComparator> &movies);

    class Movie {...};
};

In the implementation file we have:

// ActorGraph.cpp
class MovieYearComparator
{
public:
    bool operator() (const ActorGraph::Movie a, const ActorGraph::Movie b) const
    {
        // comparison operation
    }
};

ActorGraph::func(..., priority_queue<Movie, vector<Movie>, MovieYearComparator> &movies) 
{...}

However, g++ complains that MovieYearComparator was not declared in this scope, referring to the func declaration. I spelled everything correctly and this paradigm is working for other containers in the file, just not the priority queue. Note that Movie is a very small class and I therefore chose to pass it by value.


Solution

  • When you forward-declare a class, you cannot do everything with it, you only have the right to use / store reference or pointer to it, e.g.:

    struct A;
    
    struct B {
        A *_a; // Ok
        B (A &a) : _a(&a) { } // Ok
    };
    
    void f (A a) { } // Oops!
    

    The reason is simple: If you do not use a reference or a pointer, the compiler must know the size of the struct / class when using it.

    In your case, priority_queue needs to use non-reference of the Comparer, but since the first time you instantiate the template (in the definition of ActorGraph) your Comparer (MovieYearComparator) is not a complete type, priority_queue cannot be instantiated.

    You need to define the MovieYearComparator class before the first instantiation of your priority_queue, e.g.:

    class ActorGraph {
    protected:
        class Movie; // definition further below
    
        class MovieYearComparator
        {
        public:
            // Use reference since your forward-declare Movie
            bool operator() (const ActorGraph::Movie const& a, 
                             const ActorGraph::Movie const& b) const {
                // comparison operation
            }
        };
    
        int func(..., priority_queue<Movie, vector<Movie>, MovieYearComparator> &movies);
    
        class Movie {...};
    };
    

    If you want to keep your header "clean", you could only define the class in the header but implement the operator() in the cpp file:

    class ActorGraph {
    protected:
        class Movie; // definition further below
    
        struct MovieYearComparator {
            bool operator() (const ActorGraph::Movie const& a, 
                             const ActorGraph::Movie const& b) const;
        };
    
        int func(..., priority_queue<Movie, vector<Movie>, MovieYearComparator> &movies);
    
        class Movie {...};
    };
    
    // .cpp
    bool ActorGraph::MovieYearComparator::operator() (const ActorGraph::Movie const& a, 
                                                      const ActorGraph::Movie const& b) const { }
    

    Also note that since MovieYearComparator is now an inner class of ActorGraph, you do not need to make it a friend class.