verkapselt Ich habe eine Vorlage Klasse derived_object<T>
, die sehr ähnlich zu std::unique_ptr
ist. Es hat die zusätzliche Eigenschaft, dass Sie eine tiefe Kopie erstellen können, obwohl T
nur eine Basisklasse des tatsächlichen Objekts ist. Die Technik stammt von boost::any
. Hier ist der (leider lang) Inhalt der Datei derived_object.h
:Move Semantic unbeabsichtigt ersetzt durch Kopie Konstruktor in Klasse, die Zeiger
#pragma once
#include <type_traits>
#include <algorithm>
template<class Base>
class derived_object
{
public:
derived_object()
: content(nullptr)
{}
template<typename Derived>
derived_object(Derived* ptr)
: content(new holder<Derived>(ptr))
{
static_assert(std::is_copy_constructible<Derived>::value, "type of pointer must have copy constructor");
static_assert(std::is_base_of<Base, Derived>::value, "type of pointer must be derived from base type");
}
derived_object(derived_object const& other); // left unimplemented for testing
// : content(other.content ? other.content->clone() : nullptr)
// {}
derived_object& operator=(derived_object const& rhs); // left unimplemented for testing
// {
// derived_object<Base>(rhs).swap(*this);
// return *this;
// }
derived_object(derived_object&& other) noexcept
: content(other.content)
{
other.content = nullptr;
}
derived_object& operator=(derived_object&& rhs) noexcept
{
rhs.swap(*this);
derived_object<Base>().swap(rhs);
return *this;
}
~derived_object()
{
delete content;
}
Base* operator->() const
{
return content->operator Base*();
}
Base* get() const
{
return content->operator Base*();
}
derived_object& swap(derived_object& rhs) noexcept
{
std::swap(content, rhs.content);
return *this;
}
private:
class placeholder
{
public:
virtual ~placeholder() {}
virtual operator Base*() const = 0;
virtual placeholder* clone() const = 0;
};
template<typename Derived>
class holder : public placeholder
{
Derived* held;
public:
holder(Derived* der_ptr)
: held(der_ptr)
{ }
placeholder* clone() const override
{
return new holder(new Derived(*held));
}
~holder() override
{
delete held;
}
operator Base*() const override { return held; }
};
placeholder* content;
};
Dieses schönen im Allgemeinen funktioniert. Aber in einem speziellen Fall, in dem ich die Verschiebungssemantik anstelle des Kopierens verwenden möchte, schlägt mein Programm fehl. Zu Testzwecken habe ich das Kopieren in derived_object deaktiviert, indem ich die entsprechenden Methoden deklariert, aber nicht definiert habe.
Hier sind die Inhalte von classes.h
#pragma once
#include "derived_object.h"
class B1
{};
class D1 : public B1
{};
class B2
{};
class D2 : public B2
{
public:
D2(derived_object<B1> member);
derived_object<B1> member;
};
classes.cpp
#include "classes.h"
D2::D2(derived_object<B1> m)
: member(std::move(m))
{}
und main.cpp
#include "classes.h"
int main()
{
derived_object<B2> e(new D2(new D1()));
}
Kompilieren mit Clang (Version 3.8.0-2ubuntu4) über
clang++ -std=c++14 -o main.cpp.o -c main.cpp
clang++ -std=c++14 -o classes.cpp.o -c classes.cpp
clang++ main.cpp.o classes.cpp.o -o output
gibt den Linker-Fehler main.cpp:(.text._ZN2D2C2ERKS_[_ZN2D2C2ERKS_]+0x19): undefined reference to `derived_object<B1>::derived_object(derived_object<B1> const&)'
.
Aber es sollte kein Kopieren von abgeleiteten_Objekt, ich bewegte mich immer oder verwendet den Konstruktor derived_object(Derived* ptr)
.
Und seltsam, wenn ich den D2 Konstruktor inline deklarieren oder andere kleine Änderungen vornehmen, tritt der Fehler nicht auf.
Warum möchte der Compiler mein Objekt kopieren, obwohl das Verschieben möglich zu sein scheint?
Wie kann ich derived_object.h
ändern (abgesehen von der Änderung seiner Methodensignaturen)?
Es ist in der Tat die Klon-Methode, obwohl nie verwendet. Ich habe es auskommentiert, danke. Es gab andere Fälle, in denen der Linker nicht scheiterte, wahrscheinlich wegen cleverer Optimierungen. (?) Ganz gleich. – Frank