1

I am writing some code for a small design challenge and have come upon a compilation error that I'm struggling with.

The gist of the problem is to create a Building with multiple levels and the items associated with that level are determined at runtime through a CLI. I have Buildable objects (which include a Building and a Level) as well as an abstract templated LevelAddon object that implements Buildable. The concrete addons, for example an Entrance, would derive from the LevelAddon.

I've already solved the problem one way using some factory methods but am trying to solve it using the Prototype design pattern now. I have created a Cloneable interface which uses some namehiding with the goal being to call Clone() on an object to get a unique_ptr to a new object of the same type / configuration.

Because Levels contain a vector of the addons they own, and those addons can be of any type, I inserted a GenericLevelAddon class as a layer between Buildable and LevelAddon so you could store a list of the addons without knowing the derived types.

The problem is that I cannot instantiate my derived addons (e.g. Entrance) because the compiler complains that my Cloneable's Base* CloneDervived method is overridden with a type that is not covariant with Buildable.

Buildable interface:

class Buildable {
public:
    virtual ~Buildable() = default;
    std::string GetName();
    void Build();
protected:
    Buildable(const std::string&);
    virtual std::string OnGetName();
    virtual void OnAfterBuild() = 0;
    virtual void OnBuild();

    std::string name_;
};

Cloneable interface:

template <typename Base>
class Cloneable {
public:
    virtual ~Cloneable() = default;
    std::unique_ptr<Base> Clone() { std::make_unique<Base>(CloneDerived()); }
protected:
    virtual Base* CloneDerived() = 0;
};

template <typename Derived, typename Base>
class DerivedCloneable : public Cloneable<Base> {
public:
    virtual ~DerivedCloneable() = default;
    std::unique_ptr<Derived> Clone() { std::make_unique<Derived>(CloneDerived()); }
protected:
    virtual Derived* CloneDerived() override { return new Derived(*this); }
};

LevelAddons:

namespace LevelAddons {
    class GenericLevelAddon : public Buildable, public Cloneable<Buildable> {
    public:
        virtual ~GenericLevelAddon() = default;
    protected:
        GenericLevelAddon(const std::string& name) : Buildable(name) {}
    };

    template <typename Derived>
    class LevelAddon : public GenericLevelAddon, public DerivedCloneable<Derived, Buildable> {
    public:
        virtual ~LevelAddon() = default;
    protected:
        LevelAddon(const std::string& name) : GenericLevelAddon(name) {}
    };
}

Entrance:

namespace LevelAddons {
    class Entrance : public LevelAddon<Entrance> {
    public:
        Entrance() : Entrance("Default Entrance") {};
        Entrance(const std::string& name) : LevelAddon(name) {}
        virtual void OnAfterBuild() override {};
        virtual void OnBuild() override {
            std::cout << "Building " << name_ << " Entrance..." << std::endl;
        }
    };
}

Main:

int main() {
    LevelAddons::Entrance e; // Does not compile

    return 0;
}

I feel like I'm overlooking something relatively simple at this point and would love another set of eyes.

Specific error message

Error C2555 'BuildingChallenge::DerivedCloneable::CloneDerived': overriding virtual function return type differs and is not covariant from 'BuildingChallenge::Cloneable::CloneDerived'
    with
    [
        Derived=BuildingChallenge::LevelAddons::Entrance
    ]
Quentin
  • 62,093
  • 7
  • 131
  • 191
MCwiet
  • 11
  • 1
  • I remember stumbling into this issue... The gist of it is, when instantiating the CRTP the derived class is incomplete, therefore its inheritance hierarchy is unknown and covariance cannot be verified. Unfortunately I don't remember finding any solution. – Quentin Oct 08 '19 at 15:26
  • 1
    Thanks for the replies and thanks for the link, @Moia. Definitely an issue with unknown hierarchy when trying to instantiate the derived type. Ended up solving it by making Clone a helper function that could be used more generically instead of having it as a property of a class. With some of the projects I've been doing over the last couple weeks, I feel like I've been running into issues left and right with covariance on smart pointers... – MCwiet Oct 08 '19 at 17:25

0 Answers0