1

An object child derived from base

class base
{
    public:
    virtual int get() const { return 0;}
    ~virtual base(){}
    };

    class child : public base
    {
    public:
    virtual int get() const override { return 1; }
};

is sliced when added to a container e.g.

std::vector<base> bases;
bases.push_back(child());

and the code below returns 0

bases.at(0).get();

Even if the signature of push_back is:

void push_back( const T& value );
void push_back( T && value );

which shouldn't incur the object to be sliced.

For example the code below results in bad_cast

dynamic_cast<child &>(bases.at(0))

Whereas calling with child of base exhibits polymorphic behaviour

void toto(base const & e)
{
    std::cout << e.get() << std::endl;
}

which implies the object is not anymore a child; where did it get sliced? what does actually happen?

Would it be possible to setupbase or child such that is copes with being placed in the container and the object does not lose its polymorphic nature?

This is a rationale question, and it is already known that an object is copied, so please do not to suggest pointers/shared_ptr as this has been addressed elsewhere.

g24l
  • 3,055
  • 15
  • 28
  • "such that is copes with being placed in the container " please explain what you mean. – edmz Oct 29 '15 at 11:40
  • 1
    If you already *know* that an object is copied when passed to this vector, and the solution is to use pointers to retain polymorphic behaviour - I don't understand the point of this question - there is no way to retain this behaviour without pointers - this is C++ – Nim Oct 29 '15 at 11:43
  • @Nim That's true in other languages as well. – Emil Laine Oct 29 '15 at 11:45
  • @Nim certainly, if it is not possible, then answer with rationale that this is not possible, and that is a valid answer . Thank you. – g24l Oct 29 '15 at 12:14

3 Answers3

6

where did it get sliced?

Your

std::vector<base> bases;

stores plain base objects. You just can not add anything else there, only a base objects. So if ever you get an object added there, it will be a base object. If you passed another type to push_back, it will be converted to base (if possible); in your case this means sliced. I do not think that you are interested at where exactly does the slicing occur inside std::vector's functions of a specific implementation.

Would it be possible to setup base or child such that is copes with being placed in the container and the object does not lose it's polymorphic nature?

To keep polymorphic nature, you need to store pointers to bases1. And there are many ways to make it copy on addition to the container. The simplest way is to write something like

child c;
...
std::vector<base*> v;
v.push_back(new child(c)); // and yes, smart pointers are better

You may of course add wrappers for the push_back to avoid typing new each time.

1 You can not store references in the vector, but consult this question for more options.

Community
  • 1
  • 1
Petr
  • 9,812
  • 1
  • 28
  • 52
  • @Nim, oh yes, my bad. – Petr Oct 29 '15 at 11:47
  • 2
    @Nim You _can_ store references in the form of `std::reference_wrapper`. – Emil Laine Oct 29 '15 at 11:48
  • 2
    @zenith, sure - but that's cheating.. ;) (as it's internally storing a pointer anyway.. ) – Nim Oct 29 '15 at 11:54
  • The template parameter does not always tell you what happens in the class, so just saying that it is parametrized by base, does not tell much. Of course the same can be said about the signature of push_back. However let me remind that there is a signature also that moves the object into push_back. All these are a bit surprising to the inconspicuous. – g24l Oct 29 '15 at 12:20
3

Even if the signature of push_back is:

which shouldn't incur the object to be sliced.

This is true.

For example the code below results in bad_cast
dynamic_cast<child &>(bases.at(0))

Yes, it does. And dynamic_cast is not in std::.

Whereas calling with child of base exhibits polymorphic behaviour

void toto(base const & e)
{
std::cout << e.get() << std::endl;
}

Yes, it does.

where did it get sliced? what does actually happen?

The problem is not push_back per se. Slicing happens when push_back assigns what you passed to the internal object, which is not polymorphic, not when the parameter itself is bound.

To keep the polymorphic behavior you must use either pointers (preferably smart pointers) or references; however, plain references cannot be stored in a container and you have to use a wrapper for them like std::reference_wrapper.

There is a very comprehensive (and imho underrated) answer from Stephen Li about why polymorphism works this way.

Community
  • 1
  • 1
edmz
  • 8,220
  • 2
  • 26
  • 45
  • Thanks for your edits, I have accepted most, and have not actually seen the std in dynamic_cast. – g24l Oct 29 '15 at 12:17
2

This can not work: Polymorphism uses VTables which stores pointers to the methods of you class. The problem: If you copy it, the VTable pointers will be set to the base class adresses.

Additionally it can not work because the derived class can be bigger as the base class so the memory consumption can not be calculated. But a container need to know how big the objects are.

3dz9j56
  • 112
  • 7