I can't understand anything here. I expected that if I can pass a dog pointer to function taking animal pointer, I could also pass the &dog to a function that takes a pointer to pointer of Animal.
struct Animal{};
struct Dog : Animal{};
void ptrToPtr(Animal** arg){}
void refToPtr(Animal*& arg){}
void refToConstPtr(Animal* const & arg){}
void ptrToConstPtr(Animal* const * arg){}
int main(void)
{
Dog* dog;
Animal* animal;
ptrToPtr(&animal); // Works
ptrToPtr(&dog); // Argument of type Dog** is incompatible with argument of type Animal**
refToPtr(animal); // Works
refToPtr(dog); // A reference of type Animal*& (not const-qualified) cannot be initialized with a value of type Dog*
ptrToConstPtr(&animal); // Works
ptrToConstPtr(&dog); // Argument of type Dog** is incompatible with paramater of type Animal* const*
refToConstPtr(animal); // Works
refToConstPtr(dog); // Works. This is the only one that allows me to send Dog to Animal
return 0;
}
I just don't get it, can anyone explain what the reasons are for why particular cases work and others don't? Like passing the dog pointer address to the Animal**, that would be an upcast, wouldn't it?
interconvertible pointer types are pointers to objects of a derived/base type. A "pointer to a pointer" isn't interconvertible with other types (with the exception of
void*
). Same applies to references.This means that given any hierarchy below:
And the following variables:
dptr
can be converted toAnimal*
(even implicitly), likewiseaptr
can be converted toDog*
(but not implicitly). Because: up-casting in a class hierarchy is always legal, hence this will be done implicitly by ICS. However, down-casting isn't always so, so its never done implicitly)However:
ddptr
cannot be implicitly converted toAnimal**
, likewiseaptr
cannot be converted toDog**
. Because, they are two different types with no hierarchical relationship.The above explanation explains why the pointer to pointer overloads fail. That said, let's deal with the reference type overloads. From your code,
the second call doesn't work because non-const references of say
X
can only be bound to an exact glvalue object ofX
. SincerefToPtr
takes a non-const reference to aAnimal*
type, we can only pass it glvalues ofAnimal*
type, whichanimal
is, butdog
isn't.the last one works, and it's legal because
const
references of sayX
can be bound to any value catagory ofX
, including temproaries whose lifetimes are extended. Since since we can convertdog
toAnimal*
. That conversion takes place and a temporaryAnimal*
is produced whose lifetime is extended by theconst
reference.