0

I came across an old SO post about inheriting from std::vector. One of the comments made was unclear to me, why does slicing occur when you inherit from a standard library container and use a pointer/reference to the base?

Godbolt

#include <vector>
#include <iostream>

using namespace std;

class MyVec : public vector<int>
{};

void printTypeBase(vector<int>& vecObj)
{
    cout << "base parameter ref: " << typeid(vecObj).name() << endl;
}

void printTypeDerived(MyVec& vecObj)
{
    cout << "derived parameter ref: " << typeid(vecObj).name() << endl;
}


int main()
{
    auto myVec = MyVec{};

    printTypeBase(myVec);
    printTypeDerived(myVec);
}

Outputs:

base parameter ref: St6vectorIiSaIiEE

derived parameter ref: 5MyVec


When I try to dynamic_cast inside printTypeBase :

auto temp = dynamic_cast<MyVec&>(vecObj);

I get a clarifying error message:

error: 'std::vector<int, std::allocator >' is not polymorphic


In retrospect, my question should have been why is std::vector not a polymorphic type?

Mansoor
  • 2,357
  • 1
  • 17
  • 27
  • 4
    There is no slicing taking place here, as a result of using a pointer to a reference. Replace `std::vector` by `class foo` everywhere, and you'll get the same results. – Sam Varshavchik Oct 09 '20 at 12:26
  • Types in C++ are not dynamic, they are fixed and static. In the `printTypeBase` function the `vecObj` variable is a reference to `vector`, the compiler can't change that. – Some programmer dude Oct 09 '20 at 12:29
  • 1
    That's not what slicing is. `MyVec` is an empty derived class. There's nothing in it that can be sliced. If you want to see slicing, then add a data member, like an `int` for example, and then copy the object through a `vector&`. Then you get slicing of the `int` member because `vector::operator=` knows nothing about that `int` member. – Nikos C. Oct 09 '20 at 12:31
  • @SamVarshavchik [when I replace vector with a foo class](https://godbolt.org/z/a7WbG6) which has the virtual function in it, I get the ""correct"" behaviour, typeid prints "MyVec" for both functions. I understand now the point about non-polymorphic types, so this makes sense. – Mansoor Oct 09 '20 at 12:54
  • And even with a virtual function, still no slicing takes place. It only takes place when the base class is ***copied***. No copying takes place. `typeid` is not an indication of object slicing taking place. – Sam Varshavchik Oct 09 '20 at 13:17

3 Answers3

2

Standard library containers have no virtual methods and also no virtual destructor. They completely lack a vtable.

Your code fails to test what you want to test.

ypnos
  • 50,202
  • 14
  • 95
  • 141
1

Slicing isn't occurring here, however, for references to types that are not polymorphic (such as classes with no virtual methods), typeid resolves to the static type information of the argument, not the dynamic type.

Dave S
  • 20,507
  • 3
  • 48
  • 68
1

why does slicing occur

There is no slicing in your example. There is nothing in particular about standard library containers that causes slicing more than inheriting from any other type.

base parameter ref: St6vectorIiSaIiEE

error: 'std::vector<int, std::allocator >' is not polymorphic

The latter error explains the result of the typeid. Vector is not a polymorphic type. This is because it has no virtual functions. dynamic_cast is allowed only for polymorphic types, and typeid resolves to the dynamic type only in case of reference to polymorphic type.

eerorika
  • 232,697
  • 12
  • 197
  • 326