Can't upcast pointer to pointer argument

653 views Asked by At

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?

1

There are 1 answers

2
WhiZTiM On

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:

struct Animal{};
struct Dog : Animal{};

And the following variables:

Dog* dptr;
Animal* aptr;

dptr can be converted to Animal* (even implicitly), likewise aptr can be converted to Dog* (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:

Dog** ddptr;
Animal** aaptr;

ddptr cannot be implicitly converted to Animal**, likewise aptr cannot be converted to Dog**. 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,

refToPtr(animal); // Works
refToPtr(dog);      // A reference of type Animal*& (not const-qualified) cannot be initialized with a value of type Dog*

the second call doesn't work because non-const references of say X can only be bound to an exact glvalue object of X. Since refToPtr takes a non-const reference to a Animal* type, we can only pass it glvalues of Animal* type, which animal is, but dog isn't.

the last one works, and it's legal because

refToConstPtr(animal); // Works
refToConstPtr(dog);    // Works. This is the only one that allows me to send Dog to Animal

const references of say X can be bound to any value catagory of X, including temproaries whose lifetimes are extended. Since since we can convert dog to Animal*. That conversion takes place and a temporary Animal* is produced whose lifetime is extended by the const reference.