Do you think that explaining the behaviour of particular undefined
behaviour invocations is useful? The advice should be to avoid undefined
behaviour, period. Explaining how things work (such as dynamic dispatch)
can be done without resorting to explaining it in terms of undefined
behaviour.
/Leigh
Yes I do. The OP obviously has tried both options and is puzzled by the
difference in behavior (not noticing that both were undefined behavior).
By pointing out how some undefined behavior typically operates, it does
a couple of thing.
One, it points out that just because something "works" doesn't mean you
haven't committed undefined behavior (note that my description of the
non-virtual case doesn't say it WILL work, just why it might).
Two, understand why some behaviors are defined as "undefined" helps give
reality to the concept. Things are not undefined behavior just because
the writers of the standard didn't want to define it, but there is
almost always a real reason why it is impractical to define the behavior
on all implementations. Knowing this, it makes it easier to remember
what is undefined behavior, as well as to understand code that might
invoke what is undefined behavior in the C++ standard but may be defined
by another standard or the implementation.
For example, the code:
int i;
i = 30000 + 30000;
has possibly invoked undefined behavior by possible overflowing signed
integer arithmetic (if ints are only 16 bits). Knowledge that you are on
a 32 bit machine, or that (as is common), the behavior of in integer
arithmetic is reasonably well defined on this platform, allows one to be
less paranoid in the checks that need to be made to avoid/detect problems.