Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Foolproof copy overloading: how not to take care of members that should default-copy?

Tags:

c++

With the growing use of recent C++ classes such as std::unique_ptr I find a lot of classes that use them for their members cannot be copied by default. This is good, as they, well, can't be copied: those members need to be taken care of explicitly if the parent class has to be copyable.

However I find overloading copy constructors/operator to be inconvenient. Each member needs to be explicitly copied, even those that don't need special treatment. What's more, when a member is added, the code maintainer has to remember to add this member to the copy functions too. I find the probability that this code maintainer will fail to do so is close to one when that is me. (I am aware that at least one can have the operator= rely on the copy constructor).

So I was wondering if there would be efficient ways to overload the copy constructors/operator so that no code needs to be written for members that shall be default-copied.

One solution I could think of would be to split the class in two: put in a base class members that default-copy and the rest in a child class; overload copy of the child class only. I feel this is a bit inelegant and I am sure it has drawbacks.

Another would be to be wrap non-copyable objects into classes that, when copied, construct the underlying non-copyable object. This is perhaps better but it is useful only when those objects can be initialized independently of the other parent class members.

If anyone has thoughts or experience to share with how they address this or not for copy, I would be glad to hear it.

like image 616
P-Gn Avatar asked Mar 24 '15 12:03

P-Gn


1 Answers

Wrap the unique_ptr members in another type which does the special behaviour (i.e. cloning the pointee, or whatever is appropriate). Or use a clone_ptr instead of unique_ptr.

Then just declare you class' copy constructor and copy assignment operator as defaulted.

This is a general pattern that is useful when you want non-default behaviour for some members only: Don't implement the entire copy constructor manually and have to specify behaviour for every member, use a wrapper to add the special behaviour to the members that need special behaviour, and then the containing class just relies on defaults.

In fact this is just a generalization of using an RAII type like unique_ptr or even std::string as a member. Instead of having to write a destructor for the containing class which remembers to delete the pointer member, or free every char*, you make each member do the right thing, and the containing class becomes foolproof.

This is perhaps better but it is useful only when those objects can be initialized independently of the other parent class members.

It's still possible, but the wrapper might need a pointer back to the parent class, so it can reach the other members, and that does complicate things slightly.

In the case where you have related members that can't be initialized independently the related members can be grouped into another type which does the right thing for all of them together. That still makes it easier to compose them into a parent class if there are other members that can be initialized independently.

Again, you're making each sub-object independent and foolproof, so they can be composed like simple building blocks. If there are members that are not independent, they should be part of the same building block.

like image 156
Jonathan Wakely Avatar answered Oct 31 '22 14:10

Jonathan Wakely