Первой причиной сообщения об ошибке является то, что тип класса никогда не может использоваться в качестве типа для dynamic_cast
. Целевой тип dynamic_cast
всегда должен быть либо указателем на тип класса (что означает, что результатом будет нулевое значение, если приведение не выполнено), либо ссылкой на тип класса (то есть выбросить исключение, если приведение не выполнено).
Итак, улучшение №1:
my_map[0] = dynamic_cast<GameObject*>(std::unique_ptr<Enemy>().get());
Но это не сработает, потому что GameObject
является частным базовым классом Enemy
. Возможно, вы хотели использовать публичное наследование, но (при использовании class
вместо struct
) вы должны сказать так:
class Enemy : public GameObject
// ...
Далее мы обнаружим, что =
в операторе map недействителен. Левая сторона имеет тип std::unique_ptr<GameObject>
, который не имеет operator=
, который может принимать указатель GameObject*
. Но у него есть элемент reset
для установки необработанного указателя:
my_map[0].reset(dynamic_cast<GameObject*>(std::unique_ptr<Enemy>().get()));
Теперь утверждение должно скомпилироваться - но оно все равно не так.
Прежде чем понять, почему это не так, мы можем сделать упрощение. dynamic_cast
необходим для получения указателя на производный класс от указателя на базовый класс или для многих других изменений типа в более сложном дереве наследования. Но вообще не нужно получать указатель на базовый класс от указателя на производный класс: это допустимое неявное преобразование, поскольку каждый объект с типом производного класса всегда должен содержать подобъект типа базового класса, и нет " провал "дело. Так что dynamic_cast
здесь можно просто отбросить.
my_map[0].reset(std::unique_ptr<Enemy>().get());
Следующая проблема заключается в том, что std::unique_ptr<Enemy>()
создает нулевой unique_ptr
, а объект Enemy
вообще не создается. Чтобы создать фактический Enemy
, мы можем вместо этого написать std::unique_ptr<Enemy>(new Enemy)
или std::make_unique<Enemy>()
.
my_map[0].reset(std::make_unique<Enemy>().get());
Все еще не так, и немного хитрым способом. Теперь проблема в том, что созданный объект Enemy
принадлежит временному объекту std::unique_ptr<Enemy>
, возвращенному make_unique
. reset
сообщает std::unique_ptr<GameObject>
на карте, что он должен иметь указатель на тот же объект. Но в конце оператора временный std::unique_ptr<Enemy>
уничтожается и уничтожает объект Enemy
. Таким образом, на карте остается указатель на мертвый объект, который недопустим для использования - не то, что вы хотели.
Но решение здесь в том, что нам вообще не нужно возиться с get()
и reset()
. Существует operator=
, который позволяет присваивать значение std::unique_ptr<Enemy>
значению std::unique_ptr<GameObject>
, и он делает все правильно. Используется неявное преобразование из Enemy*
в GameObject*
.
my_map[0] = std::make_unique<Enemy>();
(Обратите внимание, что если у вас есть с именем std::unique_ptr<Enemy>
, вам потребуется std::move
, чтобы разрешить назначение, как в my_map[0] = std::move(enemy_ptr);
. Но std::move
выше не требуется, потому что результат из make_unique
уже является значением.)
Теперь это утверждение намного короче и более разборчиво, и на самом деле будет делать то, что вы хотите.
Комментарий также предложил возможность
my_map.emplace(0, std::make_unique<Enemy>());
Это также верно, но, возможно, есть важное отличие: если на карте уже есть объект с нулевым ключом, версия =
уничтожит и заменит старую, а версия emplace
оставит карту в покое. и только что созданный Enemy
будет уничтожен.