У меня была такая же проблема раньше.Это сводится к разрешению перегрузки вашего пользовательского конструктора Derived(Base&)
против неявно определенного конструктора копирования Derived(const Derived&);
, где неявный конструктор копирования просто выигрывает.Удаление этого не исправляет это, оно все еще участвует в разрешении перегрузки (но оно предотвращает молчание неправильной вещи).
Вот сокращенный пример:
struct Base
{
virtual ~Base();
};
struct Derived : Base
{
Derived(Base&);
Derived(const Derived&); // Implicitly or explicitly declared in any case.
};
Derived getDerived();
void test()
{
Derived d1 = getDerived();
Derived d2(d1); // copies
}
https://godbolt.org/z/aoJFlC
Есть несколько способов заставить код делать то, что вы хотите, так, как вы его написали (см. Другие ответы), но я хотел бы отметить, что вы должны быть особенно внимательны, чтобы сохранить следующего читателявашего кода из той же путаницы у вас было.Приведение к Base&
, измененная семантика конструктора копирования или что-то вроде использования Derived(Base*);
вызовет вопросы у будущих читателей.Вы можете попытаться решить это с помощью документации, но есть вероятность, что кто-то упустит это и запутается.Я бы предложил сделать намерение настолько явным и видимым, насколько это возможно, например, так:
enum class ConstructFromBase { Tag };
struct Derived : Base
{
Derived(Base&, ConstructFromBase);
Derived(const Derived&) = delete;
};
Derived getDerived();
void test()
{
Derived d1 = getDerived();
Derived d2(d1, ConstructFromBase::Tag);
}
https://godbolt.org/z/QfeuAM
Это должно сообщать намерение очень четко и практически ничего не стоит.(Есть много других способов написать и назвать такой тег, мой, вероятно, не самый канонический ...)