Clang и gcc застревают из-за перегруженной функции шаблона

Мне нужны классы Derived1 и Derived2, производные от абстрактного базового класса Base.
У меня также есть класс Container, который содержит карту уникальных указателей на объекты класса Base, потому что я хочу реализовать полиморфизм во время выполнения.
В классе Container есть функция add, которая принимает уникальный указатель на объект базового класса и добавляет его в map. Я заметил, что часто знаю конкретный тип добавляемого объекта, поэтому добавил перегруженную шаблонную функцию добавления, которая принимает произвольный объект по значению, перемещает его в уникальный указатель и вызывает другую функцию add.

Вот минимальный пример:

#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()); 
}

При попытке скомпилировать это с помощью clang 16, кажется, что он зависает и медленно использует все больше и больше оперативной памяти, пока мои 32 ГБ не заполнятся. gcc, похоже, тоже завис, но не использует все больше и больше оперативной памяти.

Я могу это исправить, просто переименовав одну из функций add, но мне все равно интересно, что здесь происходит.

Это ожидаемое поведение, потому что я нарушил какое-то зловещее правило C++, которое вам просто необходимо знать? Я думаю, что зависание компилятора — это то, чего никогда не должно происходить, независимо от ввода.

Таким образом, либо я сделал что-то, что не встречается в других базах кода, поэтому эта ошибка компилятора никогда не была замечена, либо я сделал что-то настолько необычное, что никто никогда не удосужился найти более чистое решение, чем зависание компилятора. Это будет мой второй вопрос: есть ли в моем подходе что-то настолько унидиоматичное, что это не кажется проблемой другим?

Альтернатива: template<typename T, typename...Ts> void add(std::string name, Ts&&... args) { this->add(name, std::make_unique<T>(std::forward<Ts>(args)...)); } с использованием c.add<Derived>("name"/*, constructor args */)

Jarod42 29.07.2024 12:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Этот шаблон метода:

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

Вызывает бесконечный цикл создания экземпляров методов.

Это называется инициализацией с T=Derived1,
тогда с T=std::uqniue_ptr<Derived1>,
тогда с T=std::uqniue_ptr<std::unique_ptr<Derived1>>,
и так далее.

Компилятор продолжает создавать экземпляры этих различных методов до тех пор, пока в конечном итоге у него не закончится память или он просто не застрянет в бесконечном цикле, пытаясь продолжить создание экземпляров.

Проблема возникает и в MSVC (в MSVC я в итоге получил: fatal error C1060: compiler is out of heap space).

Если вы переименуете, например. имя нешаблонного метода и вызовите его из шаблонного (как вы заметили), вы разорвете этот цикл и, следовательно, проблема не возникнет.

Это имеет смысл, спасибо, что уделили время!

lordlol 28.07.2024 11:24

Другие вопросы по теме