Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't a vector of smart pointers to an item implementing an interface covariant with that interface?

Why isn't a vector of smart pointers covariant with an interface that item implements? e.g. if I have a vector of pointers to a dog, why can't I use that as a vector of pointers to iAnimal?

#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct iAnimal
{
    virtual std::string speak() const = 0;
};

struct iMammal : public iAnimal
{
    virtual std::string speak() const = 0;
    virtual int legs() const = 0;
};


struct Dog : public iMammal
{
    std::string speak() const
    {
        return "WOOF!";
    }
    int legs() const
    {
        return 4;
    }
};


void giveMammals(std::vector<std::shared_ptr<iMammal>> an)
{
    for (const auto x : an)
    {
        x->legs();
    }
}

void giveAnimals(std::vector<std::shared_ptr<iAnimal>> an)
{
    for (const auto x : an)
    {
        x->speak();
    }
}

int main()
{   
    std::vector<std::shared_ptr<Dog>> vec1 = { std::make_shared<Dog>() };
    std::vector<std::shared_ptr<iMammal>> vec= { std::make_shared<Dog>() };
    giveAnimals(vec);
    giveMammals(vec1);
    return 0;
}
like image 247
Carbon Avatar asked Mar 05 '23 18:03

Carbon


1 Answers

The reason is that the kind of code you're proposing could be easily exploited to do a lot of nasty things. For one example, let's see how, trivially, if this code were legal (it's not) it would be possible to insert a Cat into a vector of Dog's.

struct Cat : public iMammal {
    std::string speak() const
    {
        return "meow.";
    }
    int legs() const
    {
        return 4;
    }
};

void giveAnimals(std::vector<std::shared_ptr<iAnimal>> & an) {
    //This, on its own, is perfectly legal given the type that `an` is.
    an.emplace_back(std::make_shared<Cat>());

    for (auto const& x : an)
    {
        x->speak();
    }
}

int main() {
    std::vector<std::shared_ptr<Dog>> vec = { std::make_shared<Dog>() };
    giveAnimals(vec);//Uhhhh.......
    for(auto const& dog_ptr : vec) {
        dog_ptr->speak();//These are not all going to bark!
    }
}

std::vector (and other similar library constructs) prohibit this kind of conversion exactly to prevent mistakes/errors like this.

like image 194
Xirema Avatar answered Apr 06 '23 18:04

Xirema