This is not a duplicate of Implementing the copy constructor in terms of operator= but is a more specific question. (Or so I like to think.)
Intro
Given a (hypothetical) class like this:
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 by the default generated functions, because you can neither copy construct nor copy a NonCopyable object. However, this part of the object is an implementation detail we are actually not interested in copying.
It does also does not make any sense to write a swap function for this, because the swap function could just replicate what std::swap does (minus the NonCopyable).
So if we want to copy these objects, we are left with implementing the copy-ctor and copy-operator ourselves. This is trivially done by just assigning the other members.
Question
If we need to implement copy ctor and operator, should we implement the copy ctor in terms of the copy operator, or should we "duplicate" the code with 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 for an answer would be performance vs. maintainability vs. readability.
In general, I prefer b) over a) as it explicitly avoids any default construction of members. For ints, doubles etc. that isn't a consideration, but it can be for members with expensive operations or side effects. It's more maintainable if you don't have to consider this potential cost/issue as you're adding and removing members. Initialiser lists also support references and non-default-constructable elements.
Alternatively, you could have a sub-structure for the non-"implementation detail" members and let the compiler generate copying code, along the lines:
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;
}
};