c++templatesinheritanceoverloadingunique-ptr

Clang and gcc getting stuck over overloaded template function


I have to classes Derived1 and Derived2 derived from an abstract base class Base.
I also have a class Container, that contains a map of unique pointers to objects of the Base class, because I want to do runtime polymorphism.
The Container class has a function add that takes a unique pointer to an object of the base class and adds it to the map. I noticed that I often know the concrete type of the added object, so I added an overloaded, templated add function, that takes an arbitrary object by value, moves it into a unique pointer and calls the other add function.

Here is a minimal example:

#include <map>
#include <memory>
#include <string>

class Base {
    public:
        virtual ~Base();
};

class Derived1 : Base {
};

class Derived2 : Base {
};

class Container {
    public:
        std::map<std::string, std::unique_ptr<Base>> coll;

        void add(std::string name, std::unique_ptr<Base> elem) {
            coll.emplace(name, std::move(elem));
        }

        template<typename T>
        void add(std::string name, T elem) {
            this->add(name, std::make_unique<T>(std::move(elem)));
        }
};

int main() {
    Container c;

    //a little more convenient than c.add("1", std::make_unique<Derived1>());
    c.add("1", Derived1()); 
}

When trying to compile this with clang 16, it seems to get stuck and slowly uses more and more RAM until my 32GB are full. gcc also seems to be stuck but without using more and more RAM.

I can fix it by just renaming one of the add functions, but I am still curious what is happening here.

Is this expected behavior, because I broke some ominous C++ rule you just have to know? I would think the compiler getting stuck is something that should never really happen, regardless of the input.

So either I did something that doesn't occur in other code bases, so this compiler bug was never noticed, or I did something that is so unusual that no ever bothered to make a cleaner solution than the compiler getting stuck. That would be my second question: Is their something about my approach that is so unidiomatic that this doesn't seem to be a problem for others?


Solution

  • This method template:

    template<typename T>
    void add(std::string name, T elem) {
         this->add(name, std::make_unique<T>(std::move(elem)));
    }
    

    Is causing an inifinite cycle of methods instantiations.

    It is called initialy with T=Derived1,
    then with T=std::uqniue_ptr<Derived1>,
    then with T=std::uqniue_ptr<std::unique_ptr<Derived1>>,
    and so on.

    The compiler keeps instantiating these different methods, until eventually it runs out of memory or just gets stuck in an infinite loop attempting to keep on instantiating.

    The problem happens in MSVC as well (in MSVC I eventually got: fatal error C1060: compiler is out of heap space).

    If you rename e.g. the name of the non-template method, and call it from the templated one (as you observed) you break this cycle and therefore the problem doesn't occur.