I have a vector wrapper class which is aimed to simplify polymorphism:
class Shape
{
public:
Shape(string Name) : name(Name) {}
virtual ~Shape() = default;
string name;
private:
};
class Point : public Shape
{
public:
Point(string Name, float X, float Y) : Shape(Name), x(X), y(Y) {}
float x = 0.0f;
float y = 0.0f;
private:
};
// A vector wrapper for unique ptrs, simplifies polymorphism
template <typename T>
class Vector_UniquePtrs {
public:
// Constructor with no parameters
Vector_UniquePtrs() = default;
// Constructor with initialiser list
Vector_UniquePtrs(std::initializer_list<T> items)
{
m_Items.reserve(items.size());
// fill `m_Items` from the initializer list by creating a unique_ptr from each element
std::transform(items.begin(), items.end(), std::back_inserter(m_Items), [](const T& item) {
return std::make_unique<T>(item);
});
};
// Adds a Polymorphic Item (and returns a raw ptr to it)
// usage: v.Add<sub_class>()
template <class U, class... Args>
T& Add(Args&&... args) {
// Forward args to make_unique
m_Items.emplace_back(std::make_unique<U>(std::forward<Args>(args)...));
// Return a reference
return *m_Items.back();
}
// Adds an Item (and returns a raw ptr to it)
// usage: v.Add() (equivelent to v.Add<base_class>())
template <class... Args>
T& Add(Args&&... args) {
// Forward to Add<U>
return Add<T>(std::forward<Args>(args)...);
}
// Remove item from vector
void Remove(size_t index) {
Assert_IsValid(index);
m_Items.erase(std::next(m_Items.begin(), index));
}
// Access item in vector
T& operator[](size_t index) { Assert_IsValid(index); return *m_Items[index]; } // non-const
const T& operator[](size_t index) const { Assert_IsValid(index); return *m_Items[index]; } // const
// Swaps items[n] and items[n_Next]
void ItemSwap(size_t n, size_t n_Next) {
Assert_IsValid(n);
Assert_IsValid(n_Next);
std::swap(m_Items[n], m_Items[n_Next]);
}
// Gets the number of elements in the vector
size_t Size() const { return m_Items.size(); }
protected:
// The container
std::vector<std::unique_ptr<T>> m_Items;
// Validity function
void Assert_IsValid(int index) { assert(index > -1 && index < (int)Size()); }
};
But when called with the following:
Vector_UniquePtrs<Point> v;
v.Add<Point>("A", 1.0f, 10.0f);
Shape& ref = v[0];
v.Add<Point>("B", 2.0f, 20.0f);
ref = v[1];
Point& pt1 = dynamic_cast<Point&>(ref);
Point& pt2 = dynamic_cast<Point&>(v[1]);
cout << pt1.name << ": " << pt1.x << ", " << pt1.y << endl;
cout << pt2.name << ": " << pt2.x << ", " << pt2.y << endl;
The output is: B: 1, 10 B: 2, 20
The reference is somehow still 'thinking' it is looking at the v[0] for the inherited variables and v[1] for the base variables.. Can someone explain what is happening and how I should go about to resolve this?
Thanks!
CodePudding user response:
You can't reassign a reference. ref = v[1]
copies the value of v[1]
into the object referenced by ref
. As the compiler only knows that ref
is a Shape
it calls Shape
's assignment operator and therefore only copies the Shape
members leaving the members from Point
unchanged.
If you need to change what a reference points to then you probably need to use a pointer instead.
If you want assignment to work polymorphically then you'll need to add a virtual method which does that for you.