Я написал следующий код, который использует, unique_ptr<Derived>
где unique_ptr<Base>
ожидается
class Base {
int i;
public:
Base( int i ) : i(i) {}
int getI() const { return i; }
};
class Derived : public Base {
float f;
public:
Derived( int i, float f ) : Base(i), f(f) {}
float getF() const { return f; }
};
void printBase( unique_ptr<Base> base )
{
cout << "f: " << base->getI() << endl;
}
unique_ptr<Base> makeBase()
{
return make_unique<Derived>( 2, 3.0f );
}
unique_ptr<Derived> makeDerived()
{
return make_unique<Derived>( 2, 3.0f );
}
int main( int argc, char * argv [] )
{
unique_ptr<Base> base1 = makeBase();
unique_ptr<Base> base2 = makeDerived();
printBase( make_unique<Derived>( 2, 3.0f ) );
return 0;
}
и я ожидал этот код не компилируется, потому что , согласно моему пониманию , unique_ptr<Base>
и не unique_ptr<Derived>
имеют никакого отношения типа и unique_ptr<Derived>
в действительности не является производной от unique_ptr<Base>
так что присваивание не должно работать.
Но благодаря некоторой магии это работает, и я не понимаю, почему, или даже если это безопасно. Может кто-нибудь объяснить, пожалуйста?
c++
templates
inheritance
unique-ptr
Youda008
источник
источник
unique_ptr
было бы довольно бесполезно при наличии наследстваBase
него нет виртуального деструктора.Ответы:
Немного магии вы ищете является преобразование конструктора # 6 здесь :
Это позволяет создать
std::unique_ptr<T>
неявно из истекающегоstd::unique_ptr<U>
if (для ясности приглушая удалители):Иными словами, он имитирует неявные необработанные преобразования указателей, включая преобразования из производных в базовые, и выполняет то, что вы ожидаете ™ безопасно (с точки зрения срока службы - вам все еще нужно убедиться, что базовый тип может быть удален полиморфно).
источник
Base
не вызовет деструкторDerived
, поэтому я не уверен, действительно ли это безопасно. (Это не менее безопасно, чем необработанный указатель, по общему признанию.)Потому что
std::unique_ptr
имеет конвертирующий конструктор кака также
A
Derived*
может конвертировать вBase*
неявно, тогда конструктор преобразования может быть применен для этого случая. Тогда astd::unique_ptr<Base>
может быть преобразовано изstd::unique_ptr<Derived>
неявно, как это делает необработанный указатель. (Обратите внимание, чтоstd::unique_ptr<Derived>
для построения необходимо использовать значениеstd::unique_ptr<Base>
из-за характеристикиstd::unique_ptr
.)источник
Вы можете неявно построить
std::unique_ptr<T>
экземпляр из RValue вstd::unique_ptr<S>
случаях , когдаS
конвертируется вT
. Это связано с конструктором № 6 здесь . Право собственности передается в этом случае.В вашем примере у вас есть только rvalue типа
std::uinque_ptr<Derived>
(потому что возвращаемое значениеstd::make_unique
является rvalue), и когда вы используете его как astd::unique_ptr<Base>
, вызывается упомянутый выше конструктор. Следовательно,std::unique_ptr<Derived>
рассматриваемые объекты живут только в течение короткого периода времени, то есть они создаются, а затем право собственности передаетсяstd::unique_ptr<Base>
объекту, который используется в дальнейшем.источник