I think it's impossible to static_cast a Derived reference of a value to it's Base type reference in a getIdentifer method as shown implicitly in Steve Jessop's covariance example, because then you'd be creating a "temporary reference", but I might be wrong. So instead I made a method (called getElement) that returns a possibly casted pointer, which will definitely exist.
Because you might really want to have the explicit relation between ContainerA and ContainerB (in case of duplicated code), I wrote a class-based example on how you could implement it. Else, using the templated function is probably the way to go.
If you are using structs, you don't need the public, protected and private labels, nor setters for the variables, but you of course do need some getIdentifier method to access B type elements.
Here is a demonstrating program:
A
#pragma once
#include <iostream>
class A
{
public:
A(const int& data);
friend std::ostream& operator<<(std::ostream& s, const A& a);
protected:
virtual std::ostream& toStream(std::ostream& s) const;
private:
int m_data;
};
A::A(const int& data)
: m_data{ data }
{
}
std::ostream& A::toStream(std::ostream& s) const
{
return s << m_data;
}
std::ostream& operator<<(std::ostream& s, const A& a)
{
return a.toStream(s);
}
B
#pragma once
#include "A.h"
class B : public A
{
public:
B(const int& data, const int& additionalData);
void setAdditionalData(int additionalData);
protected:
virtual std::ostream& toStream(std::ostream& s) const;
private:
int m_additinalData;
};
B::B(const int& data, const int& additionalData)
: A{ data },
m_additinalData{ additionalData }
{
}
void B::setAdditionalData(int additionalData)
{
m_additinalData = additionalData;
}
std::ostream& B::toStream(std::ostream& s) const
{
A::toStream(s);
return s << '\t' << m_additinalData;
}
ContainerA
#pragma once
#include "A.h"
#include <vector>
class ContainerA
{
public:
void push(A* a);
size_t getSize() const;
virtual A* getElement(const int& index);
virtual A* operator[](const int& index);
protected:
std::vector<A*> m_As;
};
void ContainerA::push(A* a)
{
m_As.push_back(a);
}
A* ContainerA::getElement(const int& index)
{
return m_As[index];
}
A* ContainerA::operator[](const int& index)
{
return m_As[index];
}
size_t ContainerA::getSize() const
{
return m_As.size();
}
ContainerB
#pragma once
#include "ContainerA.h"
#include "B.h" // The compiler should be able to tell that B is a subclass of A
class ContainerB : public ContainerA
{
public:
B* getElement(const int& index) override;
B* operator[](const int& index) override;
private:
int additional_data;
};
B* ContainerB::getElement(const int& index)
{
return static_cast<B*>(m_As[index]);
}
B* ContainerB::operator[](const int& index)
{
return static_cast<B*>(m_As[index]);
}
main.cpp
#include "ContainerB.h"
int main()
{
B b1{ 4, -1 };
B b2{ 5, -1 };
B b3{ 6, -1 };
ContainerB contB{};
contB.push(&b1);
contB.push(&b2);
contB.push(&b3);
B* b{ contB.getElement(0) };
b->setAdditionalData(0);
size_t size{ contB.getSize() };
for (int i{ 0 }; i < size; ++i) {
std::cout << *contB.getElement(i) << std::endl;
std::cout << *contB[i] << std::endl;
}
}
Output
4 0
4 0
5 -1
5 -1
6 -1
6 -1
Now you can pass ContainerB to a function / method that expects a ContainerA without having to store redundant data.