static_cast and std::vector

J

john

Hi,

I am attempting to replace lots of C-style casts with static_cast's in
our code base. Most of these are simple, but there are a few that I'm
not sure what to do.

Basically, the problem is shown in the code below. GetD0/GetD1 are the
original C-style casts that work fine. I would like to change these to
C++ style casts, but the GetDD0/GetDD1 functions do not compile.

Is there a way to do this? Are there any potential problems I should be
aware of?

Thanks.

#include <vector>

class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*) &m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*) &m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};
 
J

john

Hi,

I am attempting to replace lots of C-style casts with static_cast's in
our code base. Most of these are simple, but there are a few that I'm
not sure what to do.

Basically, the problem is shown in the code below. GetD0/GetD1 are the
original C-style casts that work fine. I would like to change these to
C++ style casts, but the GetDD0/GetDD1 functions do not compile.

Is there a way to do this? Are there any potential problems I should be
aware of?

Thanks.

#include <vector>

class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*) &m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*) &m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};

Both the C-style cast and the static_cast are wrong; you cannot cast a
std::vector<base*> to a std::vector<derived*>.

Instead have these getter functions return a std::vector<base*> and
downcast (using static_cast or dynamic_cast) the vector's elements
instead elsewhere in your code.

/Leigh


I do not see why the C-style cast is wrong. It works flawlessly across
a wide range of compilers and systems. I can understand the problem
with C++ static_cast, but I was hoping there was a way around it. If
there is not a valid C++ way to do this, I'll leave it as is.
 
J

john

Just because something "works" does not means that it is "correct". The
language allows you to perform the C style cast (or reinterpret_cast)
however *using* the resultant object reference by invoking a std::vector
member function results in undefined behaviour. Undefined behaviour can
"silently work" on a particular implementation but that does not mean it
is correct; it isn't, it is a bug. The basic reason why this is the case
here is due to the fact that std::vector<base*> and
std::vector<derived*> are unrelated types.

Ok. Thanks for the explanation. I may look into fixing this according
to your suggestion.

John
 
F

Fred Zwarts \(KVI\)

"john" wrote in message news:[email protected]...
Hi,

I am attempting to replace lots of C-style casts with static_cast's in
our code base. Most of these are simple, but there are a few that I'm
not sure what to do.

Basically, the problem is shown in the code below. GetD0/GetD1 are the
original C-style casts that work fine. I would like to change these to
C++ style casts, but the GetDD0/GetDD1 functions do not compile.

Is there a way to do this? Are there any potential problems I should be
aware of?

Thanks.

#include <vector>

class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*) &m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*) &m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};

Both the C-style cast and the static_cast are wrong; you cannot cast a
std::vector<base*> to a std::vector<derived*>.

Instead have these getter functions return a std::vector<base*> and
downcast (using static_cast or dynamic_cast) the vector's elements
instead elsewhere in your code.

/Leigh


I do not see why the C-style cast is wrong. It works flawlessly across a
wide range of compilers and systems.

It may work in the case of simple inheritance, but it usually fails when
multiple and/or virtual inheritance is going to be used.
 
J

Juha Nieminen

john said:
I do not see why the C-style cast is wrong.

Because it's bypassing the language's type checking mechanism, and in
this case doing so in a manner that's potentially hazardous.

std::vector<A*> and std::vector<B*> are two completely different and
independent classes that have no relation to each other (just because
they are created from the same *template* doesn't make them related;
the template is used by the compiler to create two different classes).
In principle it would be possible for the two classes to even have
completely different implementations (via template specialization).

More importantly, though, it's theoretically possible for
sizeof(A*) and sizeof(B*) to be different, which would immediately
break any code that assumed to have a std::vector<A*> but is in
reality given a std::vector<B*>.

That's the reason why static_cast won't allow you to do that: It's
a safer type checking mechanism than the C style cast (which is
basically a reinterpret_cast).
It works flawlessly across
a wide range of compilers and systems.

If you make this code only for yourself and know how the underlying
system works, then you could bypass the type checking meachanism as
you did, as long as you understand why it's wrong in principle.
 
J

Joshua Maurice

This is not "simple inheritance" as the types involved are unrelated.

Fred Zwarts meant to say that if the contained types have a simple
inheritance relationship, then as a matter of coincidence and facts of
implementations, it's likely to work (even UB programs can work in
some cases), but if the contained types are multiple or virtual
inheritance, then it starts becoming much less likely. He did not mean
to imply the vectors are related by inheritance, nor that the casting
of the OP is not UB, nor that writing UB code is sound advice.
 
T

Tobias Müller

john said:
Hi,

I am attempting to replace lots of C-style casts with static_cast's in
our code base. Most of these are simple, but there are a few that I'm not sure what to do.

Basically, the problem is shown in the code below. GetD0/GetD1 are the
original C-style casts that work fine. I would like to change these to
C++ style casts, but the GetDD0/GetDD1 functions do not compile.

Is there a way to do this? Are there any potential problems I should be aware of?

Thanks.

#include <vector>

class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*) &m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*) &m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};

Why don't you just write:
std::vector<Derived0*> m_vd0;
std::vector<Derived1*> m_vd1;
and use those without any casting?

This would be the most obvious solution and you are losing nothing.

Tobi
 
P

Pavel

john said:
Hi,

I am attempting to replace lots of C-style casts with static_cast's in our code
base. Most of these are simple, but there are a few that I'm not sure what to do.

Basically, the problem is shown in the code below. GetD0/GetD1 are the original
C-style casts that work fine. I would like to change these to C++ style casts,
but the GetDD0/GetDD1 functions do not compile.

Is there a way to do this? Are there any potential problems I should be aware of?

Thanks.

#include <vector>

class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*) &m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*) &m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};

Technically, C-style cast in this case is reinterpret_cast<>, not static_cast<>
so you have to be able to compile after converting to reinterpret_cast<>.

(I know the resulting code will stay UB and that two UBs are theoretically never
equal; but from practical perspective I would bet old and new UBs will not be
too different; and quite possible, new code will continue "work fine" in the
same situations as the old code did).

-Pavel
 
J

john

#include said:
class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*)&m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*)&m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};

Why don't you just write:
std::vector<Derived0*> m_vd0;
std::vector<Derived1*> m_vd1;
and use those without any casting?

This would be the most obvious solution and you are losing nothing.

We have looked into this, but we also really need functionality like

const std::vector<Base*>* GetBase(int which)
{ return &m_vb[which]; }

So, changing as you suggested would preclude this. Would it be possible
to replace the UB casts

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*)&m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*)&m_vb[1]; }

with some type of specialized iterator?

Thanks.
 
I

Ian Collins

#include<vector>

class Base {};
class Derived0 : public Base {};
class Derived1 : public Base {};

class Y
{
std::vector<Base*> m_vb[2];

public:

void Add(Derived0* d0) { m_vb[0].push_back(d0); }
void Add(Derived1* d1) { m_vb[1].push_back(d1); }

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*)&m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*)&m_vb[1]; }

const std::vector<Derived0*>* GetDD0()
{ return static_cast<const std::vector<Derived0*>*>(&m_vb[0]); }
const std::vector<Derived1*>* GetDD1()
{ return static_cast<const std::vector<Derived1*>*>(&m_vb[1]); }

};

Why don't you just write:
std::vector<Derived0*> m_vd0;
std::vector<Derived1*> m_vd1;
and use those without any casting?

This would be the most obvious solution and you are losing nothing.

We have looked into this, but we also really need functionality like

const std::vector<Base*>* GetBase(int which)
{ return&m_vb[which]; }

So, changing as you suggested would preclude this. Would it be possible
to replace the UB casts

const std::vector<Derived0*>* GetD0()
{ return (const std::vector<Derived0*>*)&m_vb[0]; }
const std::vector<Derived1*>* GetD1()
{ return (const std::vector<Derived1*>*)&m_vb[1]; }

with some type of specialized iterator?

What do you do with the returned vectors?

You don't have a vector of Derived0* to return, which is why others have
told you the casts are UB. If you are iterating through them, you could
add a (template) method to perform the iteration with the appropriate
cast of the vector elements (not the vector) to your container class.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top