C++26 brings us many new tools, some big ones like contracts, reflection, structured concurrency, but even many smaller useful ones.

In this articles series (2 episodes) we will investigate on “DEEP” copies. This is now easily feasible due to the following 2 constructs:

  • std::indirect
  • std::polymorphic

The use case is:

  • we want to allocate something on the heap
  • we want to be able to copy that thing, in an easy way, with manually solving the problems this inherently has

Thinking out loud of some silly examples:

  • a Zoo: collection of animals
  • a Canvas: a collection of shapes
  • a Chess Board: a collection of pieces

And for example I want to make a copy of my Chess Board.

In this article we will focus on std::polymorphic.

Our toy types are:

How did we have to do this in the past ?

When we wanted to store all our different DerivedX instances we had to store them as Base Objects, well we had to either store them as “reference” to Base, or “pointer” to Base. Since the Base class is abstract it can not be instantiated.

So once we have our vector of ‘such things’ and we wanted to copy that vector, we would run into the same problems as discussed in the previous article (copy of a pointer, or just another share with, …).

Next to that we have another problem, when it is a Derived1, we need to obtain a copy of a Derived1, when it is a Derived 2 we need to obtain a copy of a Derived2 and so on. W can stare at our vector as long as we want, we only can conclude it are Base’s.

This is typically solved by adding another pure virtual method in the Base class “clone()”, which returns a new allocated Base pointer (raw or unique_ptr), and which each derived class is implementing actually returning such a pointer to its specific type. That implementation is often very simple since it can call new (or make_unique) passing in itself, so it can be copy constructed.

And at the place we want to create a copy of our vector we need to loop over it (or use an algorithm) and call clone on each element and store in the vector’s copy the new obtained “copied” item.

That works, but it would be better if things could be more simple. And that is now possible with std::polymorphic<T>.

The solution: C++26: std::polymorphic

std::polymorphic<Base>

Let’s start by declaring our vector:

std::vector<std::polymorphic<Base>> storedItems;

And start populating it with a few entries:

and call the doSomething method, to see some activity of the elements:

And now …. time for the copy:

To continue the experiment, let’s clear the first vector, and have the doSomething method called on the entries of our copy.

They are all in there, this is a true DEEP copy.

There is more: constness preserved

With raw pointer, or even smart pointers, we can make the pointer const, but this still allows us through that const pointer to modify the pointed to object.

The new C++26 constructs also fix this, a const std::indirect (and std::polymorphic) are CONST, uppercase const 😉 , also the dynamically allocated object is const.

Let’s try it out, and see the compiler complain:

We create a copy of the first element and make that copy const.

We get the following error from gcc16:

<source>: In function 'int main()':
<source>:90:23: error: passing 'const {anonymous}::Base' as 'this' argument discards qualifiers [-fpermissive]
   90 |     aCopy->doSomething();   /// nope, won't work since non const method,  const propagates, const is preserved
      |     ~~~~~~~~~~~~~~~~~~^~
<source>:19:18: note:   in call to 'virtual void {anonymous}::Base::doSomething()'
   19 |     virtual void doSomething() = 0;   // not const on purpose
      |                  ^~~~~~~~~~~
Compiler returned: 1

Moving

Similar as with std::indirect, we can move from, and a moved from object can be determined by calling the member method : valueless_after_move. Giving true in case it was moved from, false otherwise.

Summary

We have seen, we can now make deep copies of polymorphic types we allocated on the heap, through their base class, by means of std::polymorphic, and we also get preservation of const.

No more need to have an intrusive “clone()” method.

You can play with all the code snippets mentioned above here: https://godbolt.org/z/PWv65Y8zY

Further Investigation

You can see some commented out lines, that do not compile, I am not sure if they should or not. I have some more research to carry out. Learn as we go.


Leave a Reply

Your email address will not be published. Required fields are marked *