Search code examples
c++header-filesshared-ptr

Why does the compiler not find a type in this class even though it is forward declared?


I have two classes Edge and Line which represent respectively an edge between nodes on a graph and a line associated to that edge which in the context of my programme is an object which acts as a visual representation of the edge and is used to draw the 'edge' using a GUI library. I am trying to find a way to link these two objects in my code by means of a pointers from Edge to Line and Line to Edge which will be stored on those objects. I want to do this so that I can run some algorithm on Edge objects. Each Line object corresponding to an Edge has some properties such as colour, thickness etc which are drawn by a GUI plotting library.

An example usage:

Edge e = Edge(); //create an edge
Line l = Line(e); //create a line and pass it the edge

Where the created edge object is passed by reference into the constructor of Line which then sets a pointer to this edge and also sets a pointer on Edge to this Line such that the Edge and Line have some 'knowledge' of each other:

Line::Line(Edge& e)
{
    corresponding_edge_ = make_shared<Edge>(e);
    e.setLine(make_shared<Line>(this));
};

The relevant parts of my Edge class looks like this:

The edge.h file:

//===== Edge.h =====

#pragma once
#include <memory>

using namespace std; 

class Line;

class Edge {
private:
    weak_ptr<Line> associated_line_;
public:
    Edge();
    ~Edge();
    void setLine(shared_ptr<Line> l);
    weak_ptr<Line> Line();

The edge.cpp file:

//===== Edge.cpp =====

using namespace std; 

Edge::Edge(){};

Edge::~Edge() {};

void Edge::setLine(shared_ptr<Line> l) {
    associated_line_ = l;
}

weak_ptr<Line> Edge::Line() {
    return associated_line_;
};

The relevant parts of my Line class looks like this:

The line.h file:

//===== Line.h =====

#pragma once 
#include <memory>
#include "edge.h"

using namespace std;

class Edge;

class Line {
    private:
        weak_ptr<Edge> corresponding_edge_;
    public:
        Line();
        Line(Edge& e);
        ~Line();
};

The line.cpp file:

//===== Line.cpp =====

#include "line.h"
#include <memory>

using namespace std;

Line::Line() {};

Line::Line(Edge& e)
{
    corresponding_edge_ = make_shared<Edge>(e);
    e.setLine(make_shared<Line>(this));
};

Line::~Line() {};

I have made sure to only include edge.h in the line.h header and to forward declare Line in edge.h to avoid a circular reference. However when I compile this code I get the following error:

std::shared_ptr`:`Edge::Line` is not a valid template type argument for parameter `_Ty

Why does this class try to find the type of Line on the Edge class rather than from the definition in Line.h?


Solution

  • I am new here and I hope I don't get many warnings...

    The answer for the question is that what @ChrisMM commented: you should add the include headers to the cpp files:

    #include "line.h"
    #include "edge.h"
    

    Why does this class try to find the type of Line on the Edge class rather than from the definition in Line.h?

    Once there is no full declaration, the compiler only knows the class name, not the full definition. This implies you cannot access any member or the type size.

    All those weak_ptr and shared_ptr are all template instantiations. When the template gets instantiated inside functions/methods, this often causes the full definition to become required.

    I have found few other problems in your code: When you declare:

    weak_ptr<Line> Line();
    

    Causes ambiguity. Apparently, the compiler thinks you wish to create an Edge to Line conversion. Edit: Actually, the member function name shadows the type name (thanks to @Davis Herring). This causes the compiler to use the Line member function as a template argument (thanks to @molbdnilo). Just renaming it to:

    weak_ptr<Line> getLine();
    

    Fixed this and removed ambiguity.

    Edit: Instead, some may prefer to use class before Line whenever the template is instantiated, example:

    weak_ptr<class Line> Line();
    

    Also,

    e.setLine(make_shared<Line>(this));
    

    Does not seem to work, as make_shared expects the object not the pointer. However, this should work:

    e.setLine(make_shared<Line>(*this));
    

    Edit: However, this is a bad idea... as the this pointer may not be adequate to be shared depending on how the object is allocated or deallocated: it might cause double deletions. Instead, you should make Line inherit from std::enable_shared_from_this:

    class Line : public std::enable_shared_from_this<Line>
    

    And use the following instead of make_shared for this case:

     e.setLine(this->shared_from_this());
    

    Also, the edge.h is missing the class termination, but I guess that problem was just in the question. After these changes I managed to compile it.

    Anyway... Template instantiations often cause ridiculous amount of errors and warnings that often causes confusion, even to the best of us! Best wishes!