Table of Contents
Slicing
Object slicing happens when a derived-class object is copied into a base-class object by value: only the base subobject gets copied, and everything the derived class added is silently discarded (“sliced off”). The code compiles cleanly and runs without crashing, which is exactly what makes it a dangerous, experience-only trap rather than something a beginner learns from a compiler error.
struct Base { int x = 1; virtual void print() const { std::cout << "Base\n"; } }; struct Derived : Base { int y = 2; void print() const override { std::cout << "Derived\n"; } }; void byValue(Base b) { b.print(); } // takes Base by value Derived d; byValue(d); // prints "Base", not "Derived" — d was sliced to a Base
Why virtual dispatch doesn't save it
Virtual functions dispatch based on an object's actual dynamic type, but that only works through a pointer or reference to the object, not when the object itself has been copied into a differently-typed variable. byValue(Base b) constructs a genuine, separate Base object using Base's copy constructor; the y member and the overridden print() behavior never existed on that object in the first place, there is no dynamic type left to dispatch on. This is different from the usual “did I forget virtual” mistake: slicing happens even with a fully correct virtual function setup, because the problem is the by-value parameter, not the missing keyword.
void byRef(const Base &b) { b.print(); } // takes Base by reference byRef(d); // prints "Derived" — no copy, no slicing, virtual dispatch works
Where it actually bites
Slicing shows up most often in container code and generic algorithms that quietly copy by value: pushing a Derived into a std::vector<Base> slices every element on insertion, and a function template parameter deduced or declared as Base rather than Base& slices on every call, even though nothing in the code looks wrong at a glance. The standard defense is polymorphic types should be passed and stored by reference or pointer (commonly a smart pointer like unique_ptr or shared_ptr), never by value, and a class meant to be used polymorphically should make that intent explicit by deleting or protecting its copy constructor if slicing would never be correct for it.
