C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Fwd: Extension to runtime polymorphism proposed

From: Simon Schröder <dr.simon.schroeder_at_[hidden]>
Date: Mon, 6 Apr 2026 09:20:31 +0200
> On Apr 6, 2026, at 3:35 AM, Muneem via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> Problem isn't solved using virtual functions, for example:
> Base *ptr= new Base{};
> Dereieved obj;
> Obj= virtual_clone(ptr);
> //Slicing so problem is not solved

If you are using the very basic concepts of OOP incorrectly, you get bitten. Either you write polymorphic code or you don’t. If you want to switch from polymorphic objects to a specific type, you should make sure that you are allowed to by testing with a dynamic_cast. Then slicing doesn’t happen.

You correctly started with a polymorphic object:
Base *ptr = new Base{};
However, clone() would do a polymorphic clone, i.e. it would allocate memory and return a pointer. Because otherwise we could just use a constructor and don’t need a clone function. This would mean it would make more sense to write
Derived *obj = ptr.clone();
However, this would not work either because Base::clone() returns a Base* and thus cannot be assigned to a Derived*. This is basic covariance and contravariance in OO. (The Derived::clone could still return Derived* and the code would then be valid if ptr was of type Derived*.) No slicing occurs if you hold to decades-old knowledge. If you really want a copy of a derived object (but why would you?), you’d have to write this:
if(Derived *derived = dynamic_cast<Derived*>(ptr); derived != nullptr)
{
  Derived obj = *derived; // might actually slice, but obj is not polymorphic anymore, so no important information is lost
}
Yes, C++ does not prevent accidental slicing. But if you understand OO concepts it’s really easy to avoid. This kind of polymorphism is not used in high performance code because calling ‘new’ too often is really slow.
>
>
> >Second and most importantly: the destination memory area must be of sufficient
> size to accommodate any of the runtime full objects, which by definition cannot
> be known at compile time. You are not addressing this problem. And if you come
> up with a solution for it, then it is also available for the case of calling a
> virtual function.
>
> ***Answer***
> The destination in this case would either be std::variant or T(if index is constexpr or an explicit cast specifically for this job is created). The specific cast job would also be of a massive help to implement this.

So you want the same line
auto &x = hlist.select(i);
have different semantics depending on i being a compile time known value or a runtime value? I don’t think the current grammar allows for that.

Let’s take a step back. Currently we could either write
T &x = hlist.select<i>(); // compile time known index
or
std::variant x = hlist.select(i); // runtime value

Currently, we also can use a compile time known value in the second example if select() is constexpr. Why would we take away the control for the programmer to choose either of these when using compile time known values?

One version could be implemented (with or without template parameter) in terms of the other. Using reflection we could (maybe only with future additions) automatically generate the necessary boilerplate code.
>
>
> >And again: what do virtual functions and object slicing have to do with
> anything?
>
> ***Answer***
> Because it shows virtual functions are incomplete, say you have want a list of integers, floats, doubles, and you want to do arithmetic, now you have to use either wrap each in variant, in which case you want pass them to overloads expecting this specific case. The compiler isn't given a free hand for the list representation, but rather wrapped in the constraints of a pre existing container that provides gurrenties that pivoted to other motivations. In Mr. Steve's case, it was arrays holding "wrapper clases" to multiple different container type objects. In my case it would be probably very similar to what I talked about in here: https://lists.isocpp.org/std-proposals/2026/04/17641.php.

I also fail to see why compile time known heterogeneous lists have anything to do with virtual functions and slicing. Nothing you or anyone else has proposed in this context has anything to do with this. Currently, nobody is suggesting virtual functions to solve the problem. It would clear up further discussion if we could just drop anything about virtual functions or slicing and focus on the actual proposal.
>
>
> >Same as std::variant then.
> ***ANSWER***
> Not the same as variant for the reasons that talked about in the paragraph above. Basically a new heterogeneous list would be pivoted towards representation thats best for itself rather than being in the constraints of the gurrentied behaviours of some other container. Though there would be no dynamic allocating what so ever.

You want a new type that is a std::variant but is called differently because you want additional guarantees. So, it would be a std::variant. Why don’t you want to have the guarantees for std::variant? Why can’t we change an existing type to include what you want? Why do you want two nearly identical types? One criterion for standardization is teachability: How do you teach when to use std::variant or your new type? Wouldn’t everyone want the performance guarantees of your new type? Wouldn’t that make std::variant obsolete? Couldn’t we then just call your new type std::variant?
>
> >And I don't see how you're solving this problem. This is still the "magic
> happens" portion of what you've been trying to convey for a week and it seems
> no one understands.
>
> You seem to be saying that one needs to have an overload for each of the
> possible types, but your solution removes this need. How?
> ***Answer***
> The magic portion is that you can provide overloads specifically for these value types T^ that see elements of heterogeneous coming. A even better one would be functions accepting std::variants of these T^ or T types. So the solution to the magic exists, but to take advantage of it, we need that solution to not be constraint by the gurrentied behaviours of current container(in order to make them into lists), and to see the elements of those heterogeneous coming so we can provide overloads for them.

Maybe I’ve missed it before: What is T^? This currently does not exist in C++. (And this syntax will also never make it into C++ -> see the discussion why reflection uses ^^ instead of just ^.)
>
> >We don't know if it is type-safe, because we can't tell what the type is and
> how the compiler can reason anything at compile time to prove safety, because
> we don't understand what you're proposing.
> ***Answer***
> 1. It is type safe just like std::visit in that everything is produced at compile time, but also not like solutions based of std::visits:
> 1. it's not constraint by containers meant used with std::variants.
> 2. provides more gurrenties like the one I talked about here:https://lists.isocpp.org/std-proposals/2026/04/17641.php.
> 3.unlike an array of variants, each element has a fixed type, so you can't change it. This makes it more type safe and contained, which also makes it easy to optimize, and provide gurrentied for (constexpr indexes).
> 4. Because each index has a fixed type, the compiler can further optimize for that aspect.
>
>
> Basically think of it as the better of both variants and tuples.

Variants and tuples are not comparable types. In the context of your problem a heterogeneous list could be represented by a std::tuple (which is not an array of variants, but individual types). An accessor to the elements could return a std::variant. We might propose something like this for standardization:
std::variant std::tuple::operator[](std::size_t i)
The variant should have the (unique?) types of the tuple; possibly as references. We still have std::get<>() to complement this for compile time known indices.

Another thing we could propose is that if the selected type of the std::variant is known at compile time the code would compile as if the type itself was used instead of the variant. (There is precedent in the C3 programming language for automatically unwrapping optionals.)

We might not like the current syntax of this. This could be solved by allowing operator. for std::variant such that the variant could be used like the current underlying type. This would still pose some problems if the underlying type is a built in type like int or float and we would want to do basic arithmetic (which are not operators on the built in types).

I fail to see why extensions of tuple and variant could under no circumstance work the same as what you are proposing as a new type. The advantage would be that a lot of small changes are easier to get into the standard than one big change (but there is the risk that not all small changes make it into the standard). Another advantage is that every one of these small changes can be helpful to a lot of people with entirely different problems. This also makes it more likely to be standardized.

Received on 2026-04-06 07:20:46