Click to See Complete Forum and Search --> : Casting - Is this allowed


Vedam Shashank
July 14th, 2005, 10:56 PM
The following works on my compiler (VC 6.0), but is it allowed by the standard?

Derived** → Base**

Converting Derived* → Base* works OK; why doesn't Derived** → Base** work? (http://www.parashift.com/c++-faq-lite/proper-inheritance.html#faq-21.2)

If you see in the code, he says that it is an error in C++ to do this...

Vehicle** vehiclePtrPtr = carPtrPtr;

but this works fine...

Vehicle** vehiclePtrPtr = (Vehicle**)carPtrPtr;

Is it allowed by the standard.

Paul McKenzie
July 15th, 2005, 01:51 AM
No, it doesn't "work fine". Read the following:

http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.11

Regards,

Paul McKenzie

SuperKoko
July 15th, 2005, 04:32 AM
It is not valid!
Let me explain:
A pointer to Derived can be cast to a pointer to Base.
It is an universal rule.

With most compilers (even if it is not specified by the standard, and is an implementation detail) casting a pointer to Derived to a pointer to Base, is done by doing nothing in simple inheritance cases:
That is:

class A
{
public:
int x,y;
};
class B:public A
{
public:
int anInteger;
};

In this simple case, casting from B* to A* is trivial for most compilers!
But with multiple inheritances this cannot be done!

class A
{
public:
int x,y;
};
class B
{
public:
int w;
};
class C:public A,public B
{
};

It is probable (with common implementations) that casting from C* to A* is just done without doing anything, but cast from C* to B* is done (with common implementations) by adding sizeof(B) to the C* pointer.

So, you understand that the standard can just says that Derived* can be cast to Base*, but cannot be interpreted as a pointer to Base*.

So, if you want to correctly cast a Derived** (a pointer to an array of Derived*) to a Base** (an pointer to an array of Base*), it needs casting each array item (Derived*) to a Base*.
But it cannot be done automatically, because the compiler cannot know how many items there are in the array, moreover you can use only half of these items, and let other items uninitialized, so the compiler cannot even know which pointers are valid, and which pointers are not.
Moreover the compiler could only do in-place cast, or creating a new array will produce memory leaks.

It is easy to understand that it is really totally impossible to do such conversion!

But this code:

Vehicle** vehiclePtrPtr = (Vehicle**)carPtrPtr;

Compiles correctly, because the C-style cast is probably interpreted as a reinterpret_cast, and reintepret_cast can be done from any integral or pointer to type to any other integral or pointer type (except if it removes some const or volatile attributes).

In fact, it is probable that this code (non-portable) will work, because if Car is just derived from Vehicle with standard simple-inheritance, it will works with most compiler implementations.
But you must know that it is not portable!

NMTop40
July 15th, 2005, 05:34 AM
To sum up it is not correct because a Derived* is not a kind of Base* (even though a Derived is a kind of Base).

A Base* can be pointed to a different Base* that is not a Derived *. And a Derived* cannot. So you can do something with a Base* that you can't do with a Derived* which breaks a fundamental law of polymorphism.

Now a Base* const is a type of Derived* const because it cannot be reseated to point to a different Base* object, and there is nothing you can do with it except call methods of your Base object, something you can always do with a Derived* const.

(That is a Base* const, not a const Base*).

Therefore you should be able to cast a Derived * const * to a Base * const *.

SuperKoko
July 15th, 2005, 07:36 AM
To sum up it is not correct because a Derived* is not a kind of Base* (even though a Derived is a kind of Base).

A Base* can be pointed to a different Base* that is not a Derived *. And a Derived* cannot. So you can do something with a Base* that you can't do with a Derived* which breaks a fundamental law of polymorphism.

Now a Base* const is a type of Derived* const because it cannot be reseated to point to a different Base* object, and there is nothing you can do with it except call methods of your Base object, something you can always do with a Derived* const.

(That is a Base* const, not a const Base*).

That is true

Therefore you should be able to cast a Derived * const * to a Base * const *.
But, that is false!
Look at my previous post. You need a conversion from Derived* to Base*, the compiler cannot just interpret a Derived* as Base*, except with the common implementation of non-virtual single inheritance.

For example:

#include <iostream>

using namespace std;
class Base
{
int h;
public:
void f() {cout<<h<<" ";}
Base():h(1) {}
};
class Base2
{
int x;
public:
Base2():x(2) {}
};
class Derived:public Base2,public Base
{
};

int main()
{
Derived x;
Derived *p=&x;
Derived * const *pp=&p;
(*reinterpret_cast<Base * const *>(pp))->f(); // outputs 2 with BC++ or GCC, but it produces UNDEFINED results!

return 0;
}


#include <iostream>

using namespace std;
class Base
{
int h;
public:
void f() {cout<<h<<" ";}
Base():h(1) {}
};
class Derived:public Base
{
};

int main()
{
Derived x;
Derived *p=&x;
Derived * const *pp=&p;
(*reinterpret_cast<Base * const *>(pp))->f(); // outputs 1 with BC++ or GCC, but it is UNDEFINED!

return 0;
}

Note that, replacing reinterpret_cast by static_cast leads to a compilation error on both examples.
invalid static_cast from type `Derived* const*' to type `Base* const*'

NMTop40
July 15th, 2005, 09:06 AM
"should be able to" doesn't necessarily mean "can".

Can you give me an example (without any casts at all and assuming it is legal) where converting from a Derived * const * to a Base * const * can lead to doing something illegal.
(Am I a const short? Does it need to be a Derived * const * const to Base * const * const?)

class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};


class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};

class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};

int main()
{
Cat c;
Cat * const pc = &c;
Cat * const * ppc = &pc;
Dog d;
Dog * const pd = &d;
Dog * const * ppd = &pd;
Animal * const pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal * const * ppa = ppc; // probably illegal but we can always cast it :)
**ppa = *pd; // this should be legal.
(*ppc)->makeNoise();
}

and your cat will probably go "woof woof!".

NMTop40
July 15th, 2005, 09:19 AM
I did a reinterpret_cast but my cat is going miaow! (like it should). And the assign below the "illegal" one is a slice anyway (which is meaningless. If I disabled copying on Animal it would fail to compile that line).

And I can't reassign *ppa because that's where the const is.

So basically I can't see any way I could make pointers to cats go woof woof!. Therefore it should be a safe and legal action.

RoboTact
July 15th, 2005, 09:42 AM
Base* pBase=new Base();
Derived* pDerived=new Derived();
Base** ppBase=&pBase;
Derived** ppDerived=&pDerived;
Base** ppOtherBase=(Base**)ppDerived;
Base* pOtherBase=*Base;
Now, where does ppOtherBase point? It can only point to ppDerived. It can't preserve the shift during the casting SuperKoko was talking about. And where would pOtherBase point? No way other then to the first byte of the object of type Derived as pDerived does. So, this requires that cast from Derived pointer to Base pointer doesn't change pointer. If it's not so, provided example leads to error.

NMTop40
July 15th, 2005, 09:55 AM
wiithout the const I put in I can easily get my cat to go woof woof!


class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};


class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};

class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};

int main()
{
Cat c;
Cat * pc = &c;
Cat ** ppc = &pc;
Dog d;
Dog * pd = &d;
Dog ** ppd = &pd;
Animal * pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal ** ppa = reinterpret_cast< Animal **> (ppc);
*ppa = pd; // this should be legal.
pc->makeNoise();
}


Just tested that and my cat did indeed go woof! woof! (showing why the cast is a bad one here).

stober
July 16th, 2005, 04:40 AM
Just tested that and my cat did indeed go woof! woof! (showing why the cast is a bad one here).

Not really -- it just seems that way on first glance. The line just before calling makeNoise() reset Animal pointer from cat to dog. So the cat did NOT go "woof " :)

RoboTact
July 16th, 2005, 05:01 AM
Not really -- it just seems that way on first glance. The line just before calling makeNoise() reset Animal pointer from cat to dog. So the cat did NOT go "woof " :)That's the point. Allowing Cat** to Animal** cast allowed to create pointer of type Cat* to object of type Dog, which is illegal...

stober
July 16th, 2005, 08:39 AM
That's the point. Allowing Cat** to Animal** cast allowed to create pointer of type Cat* to object of type Dog, which is illegal...

casting gets even worse than that! Try typcasting to an object that is not derived from Animal, as in this example. :thumbd: I think the gest of this thread is that the language allows typecasting because it assumes the programmer knows what he/she is doing. And in the example below, he clearly doesn't.

class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};


class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};

class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};

class Insect
{
public:
virtual void makeNoise() const = 0;
virtual ~Insect() {}
};

class BumbleBee : public Insect
{
public:
virtual void makeNoise() const
{
std::cout << "bzzzzzz!" << std::endl;
}
};

int main()
{
Cat c;
Dog d;
Cat * pc = &c;
Cat ** ppc = &pc;
Dog * pd = &d;
Dog ** ppd = &pd;
Animal * pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal ** ppa = reinterpret_cast< Animal **> (ppc);
*ppa = pd; // this should be legal.
pc->makeNoise();

BumbleBee b;
Insect* pInsect = &b;
ppa = reinterpret_cast< Animal **> (pInsect);
(*ppa)->makeNoise();




return 0;
}
class Animal
{
public:
virtual void makeNoise() const = 0;
virtual ~Animal() {}
};


class Dog : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "woof woof!" << std::endl;
}
};

class Cat : public Animal
{
public:
virtual void makeNoise() const
{
std::cout << "miaow!" << std::endl;
}
};

class Insect
{
public:
virtual void makeNoise() const = 0;
virtual ~Insect() {}
};

class BumbleBee : public Insect
{
public:
virtual void makeNoise() const
{
std::cout << "bzzzzzz!" << std::endl;
}
};

int main()
{
Cat c;
Dog d;
Cat * pc = &c;
Cat ** ppc = &pc;
Dog * pd = &d;
Dog ** ppd = &pd;
Animal * pa = pc; // legal. Can cast an Cat * const to Animal * const
Animal ** ppa = reinterpret_cast< Animal **> (ppc);
*ppa = pd; // this should be legal.
pc->makeNoise();

BumbleBee b;
Insect* pInsect = &b;
ppa = reinterpret_cast< Animal **> (pInsect);
(*ppa)->makeNoise(); // CRASH!




return 0;
}

RoboTact
July 16th, 2005, 08:47 AM
Converting from Insect* to Animal** doesn't make any sense, even in the context of this thread. They are independant classes, and even worse: you convert different levels of indirection.

stober
July 16th, 2005, 09:00 AM
Converting from Insect* to Animal** doesn't make any sense, even in the context of this thread. They are independant classes, and even worse: you convert different levels of indirection.

Of course! that's why my program crashed. This works.

ppa = reinterpret_cast< Animal **> (&pInsect);
(*ppa)->makeNoise();


and the output

woof woof!
bzzzzzz!
Press any key to continue


And I think the reason it works is because of virtual table -- replacing one function pointer with another function pointer. You can even rename the makeNoise() function in Insert and BumbleBee and the results will still be the same. It seems to be the order of the pure virtual functions, not the function names themselves.

RoboTact
July 16th, 2005, 09:37 AM
Anyway, Insect isn't derived from Animal.

stober
July 16th, 2005, 10:45 AM
*ppa = pd; // this should be legal.
ppd = (Dog**)&c;


The above two are equlivant. One is just a legal as the other -- the compiler assumed the programmer knows what he is doing.

Vedam Shashank
July 18th, 2005, 10:26 PM
No, it doesn't "work fine". Read the following:

http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.11

Regards,

Paul McKenzie
Thanks for that link

Vedam Shashank
July 18th, 2005, 11:11 PM
Thanks, Mr Superkoko and NMTop40.

It took me some time to see that what i did, results in a totally different call of a function from the v-table of the other object.

Thanks everyone for your time