I'm trying to implement an object to iterate easily an array, without static_cast
each element separately (I have to do it hundreds of times). It works when I use the classic for loop, but when I attempt to use begin()
/data()
/end()
it fails to compile. Do you know where is the mistake?
If I have these two classes:
struct Wrapper
{
int var1 = 0;
};
struct WrapperBig: public Wrapper
{
int var2 = 0;
};
And this is the object which will cast the pointers:
template <class T, int N>
class ArrayCasted final
{
public:
ArrayCasted(std::array<Wrapper*, N>& p): master(p) {}
// Works
int size() const {return static_cast<int>(master.size());}
inline T* operator[](const int index) {return static_cast<T*>(master[index]);}
// Fails
inline T* begin() const noexcept {return static_cast<T**>(master.begin());}
inline T* end() const noexcept {return static_cast<T**>(master.end());}
inline T* data() const noexcept {return static_cast<T**>(master.data());}
private:
std::array<Wrapper*, N>& master;
};
When I try to compile in Visual Studio 2019:
Wrapper* w1 = new Wrapper();
std::array<Wrapper*, 2> values {w1, w1};
ArrayCasted<WrapperBig, 2> casted {values};
// Works!
for (int i=0; i<casted.size(); ++i)
{
WrapperBig* current = casted[i];
current->var2 = -1;
}
// Fails :( C2440
for (WrapperBig* current : casted)
{
current->var2 = -1;
}
I tried the same with std::vector, but same problem. I think the mistake is in the begin
/data
/end
functions, but I'm not sure why. I tried multiple things but everything fails.
How can I do this?
This code has a number of problems.
someArray.begin()
is an iterator, which may be a pointer, but may instead be something else entirely, in which casting it to a pointer will completely fail.array<Wrapper *, 2>
, you probably want the ArrayCasted to be to a array<WrapperBig *, 2>
.WrapperBig
, you need to assure that what they point at are actually WrapperBig
objects.Putting those together we could get something on this order:
template <class T, int N>
class ArrayCasted final
{
public:
ArrayCasted(std::array<Wrapper*, N>& p): master(p) {}
// Works
int size() const {return static_cast<int>(master.size());}
inline T* operator[](const int index) {return static_cast<T*>(master[index]);}
// These work
// start from a pointer, cast it to the right kind of pointer:
inline T* begin() const noexcept {return reinterpret_cast<T*>(master.data());}
inline T* end() const noexcept {return reinterpret_cast<T*>(master.data() + master.size());}
inline T* data() const noexcept {return reinterpret_cast<T*>(master.data());}
private:
std::array<Wrapper*, N>& master;
};
int main() {
std::array<Wrapper*, 2> values;
// make sure each actually points at WrapperBig object:
for (auto & w : values) {
w = new WrapperBig;
}
ArrayCasted<WrapperBig *, 2> casted {values};
// Doesn't Fail
int i = -1;
for (WrapperBig* current : casted)
{
current->var2 = i++;
}
// auto properly deduces the type too:
for (auto c : casted) {
// When you type `c->`, Intellisense knows what comes next has to
// be either `var1` or `var2`.
std::cout << c->var2 << "\n";
}
}