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.