Y
doesn't have a copy assignment operator. It has an assignment operator from X
, but that's not the same thing. So the compiler generates one. The generated operator automatically invokes X
's assignment operator in a non-virtual manner, i.e. it actually wants to call X::operator=
, not whatever overrides it.
Z
also gets a generated assignment operator, which calls Y
's assignment operator. And so you get a reference to the actual implementation of X::operator=
, which you don't provide.
More fundamentally, though, assignment and hierarchies don't mix well. You will run into the slicing problem. Don't make classes that are part of a polymorphic hierarchy assignable (or copyable at all). It's just not a good idea.