Comment by loeg

13 hours ago

The aside about C++ seems a little confused too?

> C++ doesn’t have this problem at all, since virtual dispatch always goes through pointers and return types are always pointers too.

Uh. C++ can return objects by value. Maybe it isn't idiomatic. And I don't know if there's a convenient spelling like `Self`. But it runs into the same problem, of course -- you would need to know the concrete value type returned to have storage for it.

And Rust has the ~same solution as imagined for C++ here, I think? Have a `DynClone` trait that returns `Box<dyn Trait>` instead of `Self`.

Polymorphism in C++ can only be correct on pointers and references (which are pointers internally).

Stack-allocated objects and polymorphism only combine if the object is initialized as its true type and a pointer or reference to it is handed out. Obviously, this can create object lifetime issues if the pointer or reference escapes the lifetime of the stack frame containing the object.

  • What does your explanation have to do with the fact that C++ can express by-value returns of complex objects?

    • Everything. The issue is that the compiler won't even bother with polymorphism through a vtable for a polymorphic type (one with a vtable), unless the object is accessed through a pointer or reference.

      If you have a value of the type itself (not a pointer or reference), then polymorphism doesn't even enter the equation in C++, even if you initialize from a derived type.

      E.g. in this code:

          Base b(m_catalog.makeDerived());
          b.call_virt_func();
      

      Even if `call_virt_func` is declared virtual, it will be `Base::call_virt_func()` that is called here, guaranteed. From a language perspective, we already know that `b` is a `Base`, you literally declared and defined it that way.

      Runtime polymorphism is therefore only a game for pointers or references; it is the process of resolving the indirection that even allows for polymorphism to become a thing in C++. But this means that the compiler cannot know the actual type at compile-time for a polymorphic type, unless it can perform devirtualization as an optimization pass.

      So although C++ will certainly allow you to define a class method that returns a virtual type by value (and not by pointer or reference), even for complex types, it is almost certainly a bug to do this unless you know for sure what the type will be statically, at compile time. Because the object you create as the return value will be forced to be the return type declared at compile time, "forgetting" the fact that it was created from a type deeper in the inheritance chain. This is the 'slicing problem' that was mentioned in the earlier comment.

      1 reply →