Почему этот код не компилируется?
#include <memory>
struct Base {
};
struct Derived: Base {
int n;
};
int main() {
std::unique_ptr<Base> base_ptr = std::make_unique<Derived>(5);
}
Я просмотрел исходники unique_ptr
и похоже, что они позволяют перемещать unique_ptr
производные классы https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/unique_ptr .h#L368
Мне это нужно, чтобы я мог собирать сообщения разного типа, если есть коллекция, для дальнейшей работы с ними.
Компилятор выводит:
/usr/include/c++/13/bits/unique_ptr.h:1070:30: error: could not convert ‘std::forward<int>((* & __args#0))’ from ‘int’ to ‘arb::exchange_connector::Base’
1070 | { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
Вы не можете назначить unique_ptr. Окончательный ход()
Это не имеет ничего общего с иерархией классов, а связано с тем фактом, что Derived
— это агрегат, и вы не можете использовать make_unique
для такой инициализации агрегата.
Ваш код также не сможет скомпилироваться с std::unique_ptr<Derived>
:
std::unique_ptr<Derived> ptr = std::make_unique<Derived>(5);
Одним из решений является добавление конструктора в Derived
.
Другая проблема в вашем коде заключается в том, что для правильного разрушения Derived
с помощью указателя Base
вы в любом случае должны добавить виртуальный деструктор к Base
.
Оба показаны ниже:
#include <memory>
struct Base {
// Add a virtual destructor:
virtual ~Base() {}
};
struct Derived : Base {
// Add this constructor:
Derived(int n_) : n(n_) {}
int n;
};
int main() {
std::unique_ptr<Base> base_ptr = std::make_unique<Derived>(5);
}
Ах, ты прав, конечно.
И добавьте деструктор virtual
к Base
, чтобы избежать UB.
@ Jarod42 хорошее замечание - добавлено.
ребята, большое спасибо, я чувствую себя новичком в C++. Я думал, что у структуры будет конструктор по умолчанию. upd - действительно так, тогда почему его нельзя вызвать внутри make_unique?
@baltazar Я только что увидел ваш вопрос в комментарии о конструкторе по умолчанию для структуры. Я не уверен точно, почему (это был бы другой вопрос для языкового юриста, которым я не являюсь), но поскольку Derived
является агрегатом, он не считается конструктором, который вы можете вызвать с аргументами для членов (это то, что std::make_unique
требует ). В этом отношении агрегатная инициализация отличается от «обычной» конструкции. Подробнее об агрегатах можно посмотреть здесь.
Другой вариант — предоставить инициализатор для Base
:
std::make_unique<Derived>(Base{}, 5)
И, как упоминалось в другом ответе, не забудьте добавить виртуальный деструктор в Base
.
Вы пробовали изменить его на
std::unique_ptr<Derived> base_ptr =...
, прежде чем задали этот вопрос?