Implement copy-ctor from the point of view of the copy operator or separately?

This is not a duplicate. Implementing the copy constructor from the perspective of the = operator , but this is a more specific question. (Or so I like to think.)

Introduction

Given a (hypothetical) class as follows:

struct FooBar { long id; double valX; double valZ; long valN; bool flag; NonCopyable implementation_detail; // cannot and must not be copied // ... }; 

we cannot copy this using the default functions, because you can neither copy the construct nor copy the NonCopyable object. However, this part of the object is an implementation detail that we are not actually interested in copying.

It also makes no sense to write a swap function for this, because the swap function can simply replicate what std :: swap does (minus NonCopyable).

So, if we want to copy these objects, we need to implement instance-ctor and copy-operator. This is trivially done by simply assigning other members.

Question

If we need to implement copy ctor and operator, should we implement a copy of ctor in terms of the copy operator, or should we "duplicate" the code using an initialization list?

That is, given:

 FooBar& operator=(FooBar const& rhs) { // no self assignment check necessary id = rhs.id; valX = rhs.valX; valZ = rhs.valZ; valN = rhs.valN; flag = rhs.flag; // don't copy implementation_detail return *this; } 

Should we write a)

 FooBar(FooBar const& rhs) { *this = rhs; } 

or b)

 FooBar(FooBar const& rhs) : id(rhs.id) , valX(rhs.valX) , valZ(rhs.valZ) , valN(rhs.valN) , flag(rhs.flag) // don't copy implementation_detail { } 

Possible aspects of the response may be performance versus maintainability and readability.

+4
source share
5 answers

In general, I prefer b) over a) since it explicitly avoids any default construct for members. For ints, double, etc. This is not a consideration, but it may be for participants with expensive surgery or side effects. This is more convenient if you do not need to consider this potential cost / problem when adding and deleting members. Initializer lists also support links and non-standard elements.

Alternatively, you can have a substructure for elements that are not "implementation elements" and let the compiler generate the copy code line by line:

 struct X { struct I { int x_; int y_; } i_; int z_; X() { } X(const X& rhs) : i_(rhs.i_), z_(0) // implementation not copied { } X& operator=(const X& rhs) { i_ = rhs.i_; return *this; } }; 
+1
source

Usually you execute the assignment operator in terms of the copy constructor (@Roger Pate version):

 FooBar& operator=(FooBar copy) { swap(*this, copy); return *this; } friend void swap(FooBar &a, FooBar &b) {/*...*/} 

This requires the provision of a swap function that swaps the corresponding members (all except implementation_detail in your case).

If swap does not give up this approach, ensures that the object does not remain in some inconsistent state (only with assigned members).

However, in your case, since neither the copy constructor nor the assignment operator can abandon the implementation of the copy constructor in terms of the assignment operator (a), they are also more accurate and more convenient, and then they have almost identical code in both places (b).

+5
source

If you are really worried about std :: swap replication, why not put all the objects except the implementation detail in the structure?

 struct FooBarCore { long id; double valX; double valZ; long valN; bool flag; // ... }; struct FooBar { FooBarCore core_; NonCopyable implementation_detail; // cannot and must not be copied }; 

then you can use std :: swap for this structure in your copy function for FooBar .

 FooBar& operator=(const FooBar &src) { FooBarCore temp(src.core_) swap(temp,*this.core_); return *this; } 
+1
source

Ok, another try based on my comment on this answer .

Wrap the implementation_detail instance in a copyable class:

 class ImplementationDetail { public: ImplementationDetail() {} ImplementationDetail(const ImplementationDetail&) {} ImplementationDetail& operator=(const ImplementationDetail&) {} public: // To make the example short Uncopyable implementation_detail; }; 

and use this class in FooBar. The default Copy Constructor and Copy Assignment Operator for Foobar will work correctly.

Maybe it could even be from Uncopyable, so you are not getting implementation_detail.implementation_detail throughout your code. Or, if you control the code for the implementation_detail class, just add an empty Copy Constructor instance and an empty assignment operator.

+1
source

If Copy Constructor does not need to copy the data_ implementation and it will still be correct (I doubt the latter, but is currently valid), the implementation of the detail is redundant.

Thus, the solution is as follows: make static_stetail static and rely on the default Copy Constructor and Assignment Operator.

0
source

All Articles