简体   繁体   中英

Forward declaration not working in c++. It says initialization of incomplete type

I am learning design patterns and I am trying to implement builder pattern. For this purpose I am compiling below code with "clang++ -std=c++17" but I am getting error "error: initialization of incomplete type 'HtmlBuilder'" where I am returning in static function HtmlElement::build. How to solve this issue?

class HtmlBuilder;

class HtmlElement
{
    friend class HtmlBuilder;
    string name, text;
    vector<HtmlElement> elements;
    const size_t indent_size = 2;
    HtmlElement() {}
    HtmlElement(const string &name, const string &text): name(name), text(text) {}

public:
    string str(int indent = 0) const
    {
        ostringstream oss;
        string i(indent_size*indent, ' ');
        oss << i << "<" << name << ">" << endl;

        if (text.size() > 0)
        {
            oss << string(indent_size * (indent + 1), ' ') << text << endl;
        }

        for (const auto& e: elements)
        {
            oss << e.str(indent + 1);
        }

        oss << i << "</" << name << ">" << endl;

        return oss.str();
    }

    static HtmlBuilder build(const string& root_name)
    {
        return {root_name};
    }
};

class HtmlBuilder
{
    HtmlElement root;

public:
    HtmlElement build()
    {
        return root;
    }

    HtmlBuilder(const string& root_name)
    {
        root.name = root_name;
    }

    HtmlBuilder& add_child(const string& child_name, const string& child_text)
    {
        HtmlElement e{child_name, child_text};
        root.elements.emplace_back(e);

        return *this;
    }

    string str() const
    {
        return root.str();
    }
};

int main(int argc, char const *argv[])
{
    HtmlBuilder builder("ul");
    builder.add_child("li", "apple").add_child("li", "orange");
    cout << builder.str() << endl;

    return 0;
}

Edit - build returns object instead of reference.

As the first comment points out you need to separate the declaration from the definition , otherwise the compiler is forced to tackle both at once, as they're given.

In your original code:

class HtmlBuilder;

class HtmlElement
{
    friend class HtmlBuilder;

public:
    static HtmlBuilder build(const string& root_name)
    {
        return {root_name};
    }
};

Here the compiler is being instructed to construct and return something it only knows by name, it knows nothing about how to create one of these. That's where you get the error. If instead you split this up and have a header file like:

// html_element.h

class HtmlBuilder;

class HtmlElement
{
    friend class HtmlBuilder;

public:
    static HtmlBuilder build(const string& root_name);
};

Here it's understood that something called HtmlBuilder is involved, and it's not clear what that is, but that's fine so long as it's eventually explained.

Then later in your implementation file :

#include "html_builder.h"
#include "html_element.h"

HtmlBuilder HtmlElement::build(const string& root_name)
{
    return {root_name};
}

This means it doesn't need to know exactly how to make one, it just has the general idea, and by the time your implementation is addressed it's all clear because the header file has been processed.

Note that you should not return a reference to a temporary as JaMiT points out, so I've removed that from this code.

It's a good practice to split declarations ( .h or .hpp as you prefer) from definitions/implementations ( .cpp) to avoid traps like this. It also makes it a lot easier to find things when working with your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM