Safe assignment and copy-and-swap idiom [closed] - c++

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
I'm learning c++ and I recently learned (here in stack overflow) about the copy-and-swap idiom and I have a few questions about it. So, suppose I have the following class using a copy-and-swap idiom, just for example:
class Foo {
private:
int * foo;
int size;
public:
Foo(size_t size) : size(size) { foo = new int[size](); }
~Foo(){delete foo;}
Foo(Foo const& other){
size = other.size;
foo = new int[size];
copy(other.foo, other.foo + size, foo);
}
void swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
}
Foo& operator=(Foo g) {
g.swap(*this);
return *this;
}
int& operator[] (const int idx) {return foo[idx];}
};
My question is, suppose I have another class that have a Foo object as data but no pointers or other resources that might need custom copying or assignment:
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar const& other) : bar(other.bar) {};
Bar& operator=(Bar other) {bar = other.bar;}
};
Now I have a series of questions:
Are the methods and constructors as implemented above for the Bar class safe? Having used the copy-and-swap for Foo make me sure that no harm can be done when assigning or copying Bar?
Passing the argument by reference in the copy constructor and in swap is mandatory?
Is it right to say that when the argument of operator= is passed by value, the copy constructor is called for this argument to generate a temporary copy of the object, and that it is this copy which is then swapped with *this? If I passed by reference in operator= I would have a big problem, right?
Are there situations in which this idiom fails to provide complete safety in copying and assigning Foo?

As much as possible, you should initialize the members of your class in the initializer list. That will also take care of the bug I told you about in the comment. With this in mind, your code becomes:
class Foo {
private:
int size;
int * foo;
public:
Foo(size_t size) : size(size), foo(new int[size]) {}
~Foo(){delete[] foo;} // note operator delete[], not delete
Foo(Foo const& other) : size(other.size), foo(new int[other.size]) {
copy(other.foo, other.foo + size, foo);
}
Foo& swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
return *this;
}
Foo& operator=(Foo g) {
return swap(g);
}
int& operator[] (const int idx) {return foo[idx];}
};
and
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar const& other) : bar(other.bar) { }
Bar& swap(Bar &other) { bar.swap(other.bar); return *this; }
Bar& operator=(Bar other) { return swap(other); }
}
which uses the same idiom throughout
note
as mentioned in a comment on the question, Bar's custom copy constructors etc. are unnecessary, but we'll assume Bar has other things as well :-)
second question
Passing by reference to swap is needed because both instances are changed.
Passing by reference to the copy constructor is needed because if passing by value, you'd need to call the copy constructor
third question
yes
fourth question
no, but it is not always the most efficient way of doing things

1 - Are the methods and constructors as implemented above for the Bar class safe? Having used the copy-and-swap for Foo make me sure that no harm can be done when assigning or copying Bar?
Regarding the copy-ctor: that's always safe (all-or-nothing). It either completes (all), or it throws an exception (nothing).
If your class is only made up of one member (i.e. no base classes as well), the assignment operator will be just as safe as that of the member's class. If you have more then one member, the assignment operator will no longer be "all or nothing". The second member's assignment operator could throw an exception, and in that case the object will be assigned "half way". That means you need to implement copy-and-swap again in the new class to get "all or nothing" assignment.
However it will still be "safe", in the sense that you won't leak any resources. And of course the state of each member individually will be consistent - just the state of the new class won't be consistent, because one member was assigned and the other was not.
2 - Passing the argument by reference in the copy constructor and in swap is mandatory?
Yes, passing by reference is mandatory. The copy constructor is the one that copies objects, so it cannot take it's argument by value, since that would mean the argument has to be copied. This would lead to infinite recursion. (The copy-ctor would be called for the argument, which would mean calling the copy-ctor for the argument, which would mean ...). For swap, the reason is another: if you were to pass the argument by value, you could never use swap to really exchange the contents of two objects - the "target" of the swap would be a copy of the object originally passed in, which would be destroyed immediately.
3 - Is it right to say that when the argument of operator= is passed by value, the copy constructor is called for this argument to generate a temporary copy of the object, and that it is this copy which is then swapped with *this? If I passed by reference in operator= I would have a big problem, right?
Yes, that's right. However it's also quite common to take the argument by reference-to-const, construct a local copy and then swap with the local copy. The by reference-to-const way has some drawbacks however (it disables some optimizations). If you're not implementing copy-and-swap though, you should probably pass by reference-to-const.
4 - Are there situations in which this idiom fails to provide complete safety in copying and assigning Foo?
None that I know of. Of course one can always make things fail with multi-threading (if not properly synchronized), but that should be obvious.

This is a classic example of where following an idiom leads to unnecessary performance penalties (premature pessimization). This isn't your fault. The copy-and-swap idiom is way over-hyped. It is a good idiom. But it should not be followed blindly.
Note: One of the most expensive things you can do on a computer is allocate and deallocate memory. It is worthwhile to avoid doing so when practical.
Note: In your example, the copy-and-swap idiom always performs one deallocation, and often (when the rhs of the assignment is an lvalue) one allocation as well.
Observation: When size() == rhs.size(), no deallocation or allocation need be done. All you have to do is a copy. This is much, much faster.
Foo& operator=(const Foo& g) {
if (size != g.size)
Foo(g).swap(*this);
else
copy(other.foo, other.foo + size, foo);
return *this;
}
I.e. check for the case where you can recycle resources first. Then copy-and-swap (or whatever) if you can't recycle your resources.
Note: My comments do not contradict the good answers given by others, nor any correctness issues. My comment is only about a significant performance penalty.

Related

Should the Copy-and-Swap Idiom become the Copy-and-Move Idiom in C++11?

As explained in this answer, the copy-and-swap idiom is implemented as follows:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
By having a value of MyClass as parameter for operator=, the parameter can be constructed by either the copy constructor or the move constructor. You can then safely extract the data from the parameter. This prevents code duplication and assists in exception safety.
The answer mentions you can either swap or move the variables in the temporary. It primarily discusses swapping. However, a swap, if not optimised by the compiler, involves three move operations, and in more complex cases does additional extra work. When all you want, is to move the temporary into the assigned-to object.
Consider this more complex example, involving the observer pattern. In this example, I've written the assignment operator code manually. Emphasis is on the move constructor, assignment operator and swap method:
class MyClass : Observable::IObserver
{
private:
std::shared_ptr<Observable> observable;
public:
MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
friend void swap(MyClass& first, MyClass& second)
{
//Checks for nullptr and same observable omitted
using std::swap;
swap(first.observable, second.observable);
second.observable->unregisterObserver(first);
first.observable->registerObserver(first);
first.observable->unregisterObserver(second);
second.observable->registerObserver(second);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
}
Clearly, the duplicated part of the code in this manually written assignment operator is identical to that of the move constructor. You could perform a swap in the assignment operator and the behaviour would be right, but it would potentially perform more moves and perform an extra registration (in the swap) and unregistration (in the destructor).
Wouldn't it make much more sense to reuse the move constructor's code in stead?
private:
void performMoveActions(MyClass&& other)
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
public:
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
performMoveActions(other);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
performMoveActions(other);
}
It looks to me like this approach is never inferior to the swap approach. Am I right in thinking that the copy-and-swap idiom would be better off as the copy-and-move idiom in C++11, or did I miss something important?
First of all, it is generally unnecessary to write a swap function in C++11 as long as your class is movable. The default swap will resort to moves:
void swap(T& left, T& right) {
T tmp(std::move(left));
left = std::move(right);
right = std::move(tmp);
}
And that's it, the elements are swapped.
Second, based on this, the Copy-And-Swap actually still holds:
T& T::operator=(T const& left) {
using std::swap;
T tmp(left);
swap(*this, tmp);
return *this;
}
// Let's not forget the move-assignment operator to power down the swap.
T& T::operator=(T&&) = default;
Will either copy and swap (which is a move) or move and swap (which is a move), and should always achieve close to the optimum performance. There might be a couple redundant assignments, but hopefully your compiler will take care of it.
EDIT: this only implements the copy-assignment operator; a separate move-assignment operator is also required, though it can be defaulted, otherwise a stack overflow will occur (move-assignment and swap calling each other indefinitely).
Give each special member the tender loving care it deserves, and try to default them as much as possible:
class MyClass
{
private:
BigClass data;
std::unique_ptr<UnmovableClass> dataPtr;
public:
MyClass() = default;
~MyClass() = default;
MyClass(const MyClass& other)
: data(other.data)
, dataPtr(other.dataPtr ? new UnmovableClass(*other.dataPtr)
: nullptr)
{ }
MyClass& operator=(const MyClass& other)
{
if (this != &other)
{
data = other.data;
dataPtr.reset(other.dataPtr ? new UnmovableClass(*other.dataPtr)
: nullptr);
}
return *this;
}
MyClass(MyClass&&) = default;
MyClass& operator=(MyClass&&) = default;
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, second.data);
swap(first.dataPtr, second.dataPtr);
}
};
The destructor could be implicitly defaulted above if desired. Everything else needs to be explicitly defined or defaulted for this example.
Reference: http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf
The copy/swap idiom will likely cost you performance (see the slides). For example ever wonder why high performance / often used std::types like std::vector and std::string don't use copy/swap? Poor performance is the reason. If BigClass contains any std::vectors or std::strings (which seems likely), your best bet is to call their special members from your special members. The above is how to do that.
If you need strong exception safety on the assignment, see the slides for how to offer that in addition to performance (search for "strong_assign").
It's been a long time since I asked this question, and I've known the answer for a while now, but I've put off writing the answer for it. Here it is.
The answer is no. The Copy-and-swap idiom should not become the Copy-and-move idiom.
An important part of Copy-and-swap (which is also Move-construct-and-swap) is a way to implement assignment operators with safe cleanup. The old data is swapped into a copy-constructed or move-constructed temporary. When the operation is done, the temporary is deleted, and its destructor is called.
The swap behaviour is there to be able to reuse the destructor, so you don't have to write any cleanup code in your assignment operators.
If there's no cleanup behaviour to be done and only assignment, then you should be able to declare the assignment operators as default and copy-and-swap isn't needed.
The move constructor itself usually doesn't require any clean-up behaviour, since it's a new object. The general simple approach is to make the move constructor invoke the default constructor, and then swap all the members with the move-from object. The moved-from object will then be like a bland default-constructed object.
However, in this question's observer pattern example, that's actually an exception where you have to do extra cleanup work because references to the old object need to be changed. In general, I would recommend making your observers and observables, and other design constructs based around references, unmovable whenever possible.

Is the copy and swap idiom still useful in C++11

I refer to this question:
What is the copy-and-swap idiom?
Effectively, the above answer leads to the following implementation:
class MyClass
{
public:
friend void swap(MyClass & lhs, MyClass & rhs) noexcept;
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { swap(*this, rhs); }
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
};
void swap( MyClass & lhs, MyClass & rhs )
{
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
}
However, notice that we could eschew the swap() altogether, doing the following:
class MyClass
{
public:
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { *this = std::forward<MyClass>(rhs); }
MyClass & operator=(MyClass rhs)
{
/* put swap code here */
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
// :::
return *this;
}
};
Note that this means that we will no longer have a valid argument dependent lookup on std::swap with MyClass.
In short is there any advantage of having the swap() method.
edit:
I realized there is a terrible mistake in the second implementation above, and its quite a big thing so I will leave it as-is to instruct anybody who comes across this.
if operator = is defined as
MyClass2 & operator=(MyClass2 rhs)
Then whenever rhs is a r-value, the move constructor will be called. However, this means that when using:
MyClass2(MyClass2 && rhs)
{
//*this = std::move(rhs);
}
Notice you end up with a recursive call to the move constructor, as operator= calls the move constructor...
This is very subtle and hard to spot until you get a runtime stack overflow.
Now the fix to that would be to have both
MyClass2 & operator=(const MyClass2 &rhs)
MyClass2 & operator=(MyClass2 && rhs)
this allows us to define the copy constructors as
MyClass2(const MyClass2 & rhs)
{
operator=( rhs );
}
MyClass2(MyClass2 && rhs)
{
operator=( std::move(rhs) );
}
Notice that you write the same amount of code, the copy constructors come "for-free" and you just write operator=(&) instead of the copy constructor and operator=(&&) instead of the swap() method.
First of all, you're doing it wrong anyway. The copy-and-swap idiom is there to reuse the constructor for the assignment operator (and not the other way around), profiting from already properly constructing constructor code and guaranteeing strong exception safety for the assignment operator. But you don't call swap in the move constructor. In the same way the copy constructor copies all data (whatever that means in the given context of an individual class), the move constructor moves this data, your move constructor constructs and assigns/swaps:
MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
And this would in your alternative version just be
MyClass(const MyClass & rhs) : x(rhs.x) {}
MyClass(MyClass && rhs) : x(std::move(rhs.x)) {}
MyClass & operator=(MyClass rhs) { using std::swap; swap(x, rhs.x); return *this; }
Which doesn't exhibit the severe error introduced by calling the assignment operator inside the constructor. You should never ever call the assignment operator or swap the whole object inside a constructor. Constructors are there to care for construction and have the advantage of not having to care for the, well, destruction of the previous data, since that data doesn't exist yet. And likewise can constructors handle types not default constructible and last but not least often direct construction can be more performant than defualt construction followed by assignment/swap.
But to answer your question, this whole thing is still the copy-and-swap idiom, just without an explicit swap function. And in C++11 it is even more useful because now you have implemented both copy and move assignment with a single function.
If the swap function is still of value outside of the assignment operator is an entirely different question and depends if this type is likely to be swapped, anyway. In fact in C++11 types with proper move semantics can just be swapped sufficiently efficient using the default std::swap implementation, often eliminating the need for an additional custom swap. Just be sure not to call this default std::swap inside of your assignment operator, since it does a move assignment itself (which would lead to the same problems as your wrong implementation of the move constructor).
But to say it again, custom swap function or not, this doesn't change anything in the usefulness of the copy-and-swap idiom, which is even more useful in C++11, eliminating the need to implement an additional function.
You're certainly not considering the whole picture. Your code re-uses constructors for different assignment operators, the original re-uses assignment operators for different constructors. This is basically the same thing, all you've done is shift it around.
Except that since they write constructors, they can deal with non-default-constructible types or types whose values are bad if not initialized explicitly like int or are plain expensive to default-construct or where the default-constructed members are not valid to destruct (for example, consider a smart pointer- an uninitialized T* leads to a bad delete).
So basically, all you've achieved is the same principle but in a decidedly worse place. Oh, and you had to define all four functions, else mutual recursion, whereas the original copy-and-swap only defined three functions.
The validity of the reasons (if any) to use the copy-and-swap idiom to implement copy assignment are the same in C++11 as they are in previous versions.
Also note that you should use std::move on member variables in the move constructor, and you should use std::move on any rvalue references that are function parameters.
std::forward should only be used for template parameter references of the form T&& and also auto&& variables (which both may be subject to reference folding into lvalue references during type deduction) to preserve their rvalueness or lvalueness as appropriate.

Does it make sense to use the move-and-swap idiom on a movable and non-copyable class

If I have a class such as
class Foo{
public:
Foo(){...}
Foo(Foo && rhs){...}
operator=(Foo rhs){ swap(*this, rhs);}
void swap(Foo &rhs);
private:
Foo(const Foo&);
// snip: swap code
};
void swap(Foo& lhs, Foo& rhs);
Does it make sense to implement operator= by value and swap if I don't have a copy constructor? It should prevent copying my objects of class Foo but allow moves.
This class is not copyable so I shouldn't be able to copy construct or copy assign it.
Edit
I've tested my code with this and it seems to have the behaviour I want.
#include <utility>
#include <cstdlib>
using std::swap;
using std::move;
class Foo{
public: Foo():a(rand()),b(rand()) {}
Foo(Foo && rhs):a(rhs.a), b(rhs.b){rhs.a=rhs.b=-1;}
Foo& operator=(Foo rhs){swap(*this,rhs);return *this;}
friend void swap(Foo& lhs, Foo& rhs){swap(lhs.a,rhs.a);swap(lhs.b,rhs.b);}
private:
//My compiler doesn't yet implement deleted constructor
Foo(const Foo&);
private:
int a, b;
};
Foo make_foo()
{
//This is potentially much more complicated
return Foo();
}
int main(int, char*[])
{
Foo f1;
Foo f2 = make_foo(); //move-construct
f1 = make_foo(); //move-assign
f2 = move(f1);
Foo f3(move(f2));
f2 = f3; // fails, can't copy-assign, this is wanted
Foo f4(f3); // fails can't copy-construct
return 0;
}
Move-and-swap is indeed reasonable. If you disable the copy constructor, then the only way that you can invoke this function is if you were to construct the argument with the move constructor. This means that if you write
lhs = rhs; // Assume rhs is an rvalue
Then the constructor of the argument to operator = will be initialized with the move constructor, emptying rhs and setting the argument to the old value of rhs. The call to swap then exchanges lhs's old value and rhs's old value, leaving lhs holding rhs's old value. Finally, the destructor for the argument fires, cleaning up lhs's old memory. As a note, this really isn't copy-and-swap as much as move-and-swap.
That said, what you have now isn't correct. The default implementation of std::swap will internally try to use the move constructor to move the elements around, which results in an infinite recursion loop. You'd have to overload std::swap to get this to work correctly.
You can see this online here at ideone.
For more information, see this question and its discussion of the "rule of four-and-a-half."
Hope this helps!
I think this is fine, but I don't really understand why you wouldn't just do:
operator=(Foo&& rhs) // pass by rvalue reference not value
And save yourself a move.
What follows is opinion, and I am not really up on the 0x standard, but I think I have fairly solid reasoning backing me up.
No. In fact, it would be proper not to support assignment at all.
Consider the semantics:
"assign" means "cause B, which exists already, to be identical to A".
"copy" means "create B, and cause it to be identical to A".
"swap" means "cause B to be identical to what A was, and simultaneously cause A to be identical to what B was".
"move" means "cause B to be identical to what A was, and destroy A."
If we cannot copy, then we cannot copy-and-swap. Copy-and-swap is meant to be a safe way of implementing assignment: we create C which is identical to A, swap it with B (so that C is now what B was, and B is identical to A), and destroy C (cleaning up the old B data). This simply doesn't work with move-and-swap: we must not destroy A at any point, but the move will destroy it. Further, moving doesn't create a new value, so what happens is we move A into B, and then there is nothing to swap with.
Besides which - the reason for making the class noncopyable is surely not because "create B" will be problematic, but because "cause it to be identical to A" will be problematic. IOW, if we can't copy, why should we expect to be able to assign?

Implement copy-ctor in terms of copy-operator or separately?

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.
Normally you implement assignment operator in terms of copy constructor (#Roger Pate's version):
FooBar& operator=(FooBar copy) { swap(*this, copy); return *this; }
friend void swap(FooBar &a, FooBar &b) {/*...*/}
This requires providing a swap function which swaps relevant members (all except implementation_detail in your case).
If swap doesn't throw this approach guarantees that object is not left in some inconsistent state (with only part members assigned).
However in your case since neither copy constructor, nor assignment operator can throw implementing copy constructor in terms of assignment operator (a) is also fine and is more maintainable then having almost identical code in both places (b).
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;
}
};
If you're really bothered about replicating std::swap, why not put everything other than the implementation detail into a struct?
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 struct in your copy function for FooBar.
FooBar& operator=(const FooBar &src) {
FooBarCore temp(src.core_)
swap(temp,*this.core_);
return *this;
}
Okay, another try, based on my comment to this answer.
Wrap the implementation_detail 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 your FooBar. The default generated Copy Constructor and Copy Assignment Operator for Foobar will work correctly.
Maybe it could even derive from Uncopyable so you don't get implementation_detail.implementation_detail all over your code. Or if you control the code to the implementation_detail class, just add the empty Copy Constructor and empty Assignment Operator.
If the Copy Constructor does not need to copy implementation_detail and still will be correct (I doubt the latter, but let's assume it for the moment), the implementation_detail is superfluous.
So the solution seems to be: make the implementation_detail static and rely on the default generated Copy Constructor and Assignment Operator.

Is it bad form to call the default assignment operator from the copy constructor?

Consider a class of which copies need to be made. The vast majority of the data elements in the copy must strictly reflect the original, however there are select few elements whose state is not to be preserved and need to be reinitialized.
Is it bad form to call a default assignment operator from the copy constructor?
The default assignment operator will behave well with Plain Old Data( int,double,char,short) as well user defined classes per their assignment operators. Pointers would need to be treated separately.
One drawback is that this method renders the assignment operator crippled since the extra reinitialization is not performed. It is also not possible to disable the use of the assignment operator thus opening up the option of the user to create a broken class by using the incomplete default assignment operator A obj1,obj2; obj2=obj1; /* Could result is an incorrectly initialized obj2 */ .
It would be good to relax the requirement that to a(orig.a),b(orig.b)... in addition to a(0),b(0) ... must be written. Needing to write all of the initialization twice creates two places for errors and if new variables (say double x,y,z) were to be added to the class, initialization code would need to correctly added in at least 2 places instead of 1.
Is there a better way?
Is there be a better way in C++0x?
class A {
public:
A(): a(0),b(0),c(0),d(0)
A(const A & orig){
*this = orig; /* <----- is this "bad"? */
c = int();
}
public:
int a,b,c,d;
};
A X;
X.a = 123;
X.b = 456;
X.c = 789;
X.d = 987;
A Y(X);
printf("X: %d %d %d %d\n",X.a,X.b,X.c,X.d);
printf("Y: %d %d %d %d\n",Y.a,Y.b,Y.c,Y.d);
Output:
X: 123 456 789 987
Y: 123 456 0 987
Alternative Copy Constructor:
A(const A & orig):a(orig.a),b(orig.b),c(0),d(orig.d){} /* <-- is this "better"? */
As brone points out, you're better off implementing assignment in terms of copy construction. I prefer an alternative idiom to his:
T& T::operator=(T t) {
swap(*this, t);
return *this;
}
It's a bit shorter, and can take advantage of some esoteric language features to improve performance. Like any good piece of C++ code, it also has some subtleties to watch for.
First, the t parameter is intentionally passed by value, so that the copy constructor will be called (most of the time) and we can modify is to our heart's content without affecting the original value. Using const T& would fail to compile, and T& would trigger some surprising behaviour by modifying the assigned-from value.
This technique also requires swap to be specialized for the type in a way that doesn't use the type's assignment operator (as std::swap does), or it will cause an infinite recursion. Be careful of any stray using std::swap or using namespace std, as they will pull std::swap into scope and cause problems if you didn't specialize swap for T. Overload resolution and ADL will ensure the correct version of swap is used if you have defined it.
There are a couple of ways to define swap for a type. The first method uses a swap member function to do the actual work and has a swap specialization that delegates to it, like so:
class T {
public:
// ....
void swap(T&) { ... }
};
void swap(T& a, T& b) { a.swap(b); }
This is pretty common in the standard library; std::vector, for example, has swapping implemented this way. If you have a swap member function you can just call it directly from the assignment operator and avoid any issues with function lookup.
Another way is to declare swap as a friend function and have it do all of the work:
class T {
// ....
friend void swap(T& a, T& b);
};
void swap(T& a, T& b) { ... }
I prefer the second one, as swap() usually isn't an integral part of the class' interface; it seems more appropriate as a free function. It's a matter of taste, however.
Having an optimized swap for a type is a common method of achieving some of the benefits of rvalue references in C++0x, so it's a good idea in general if the class can take advantage of it and you really need the performance.
With your version of the copy constructor the members are first default-constructed and then assigned.
With integral types this doesn't matter, but if you had non-trivial members like std::strings this is unneccessary overhead.
Thus, yes, in general your alternative copy constructor is better, but if you only have integral types as members it doesn't really matter.
Essentially, what you are saying is that you have some members of your class which don't contribute to the identity of the class. As it currently stands you have this expressed by using the assignment operator to copy class members and then resetting those members which shouldn't be copied. This leaves you with an assignment operator that is inconsistent with the copy constructor.
Much better would be to use the copy and swap idiom, and express which members shouldn't be copied in the copy constructor. You still have one place where the "don't copy this member" behaviour is expressed, but now your assignment operator and copy constructor are consistent.
class A
{
public:
A() : a(), b(), c(), d() {}
A(const A& other)
: a(other.a)
, b(other.b)
, c() // c isn't copied!
, d(other.d)
A& operator=(const A& other)
{
A tmp(other); // doesn't copy other.c
swap(tmp);
return *this;
}
void Swap(A& other)
{
using std::swap;
swap(a, other.a);
swap(b, other.b);
swap(c, other.c); // see note
swap(d, other.d);
}
private:
// ...
};
Note: in the swap member function, I have swapped the c member. For the purposes of use in the assignment operator this preserves the behaviour to match that of the copy constructor: it re-initializes the c member. If you leave the swap function public, or provide access to it through a swap free function you should make sure that this behaviour is suitable for other uses of swap.
Personally I think the broken assignment operator is killer. I always say that people should read the documentation and not do anything it tells them not to, but even so it's just too easy to write an assignment without thinking about it, or use a template which requires the type to be assignable. There's a reason for the noncopyable idiom: if operator= isn't going to work, it's just too dangerous to leave it accessible.
If I remember rightly, C++0x will let you do this:
private:
A &operator=(const A &) = default;
Then at least it's only the class itself which can use the broken default assignment operator, and you'd hope that in this restricted context it's easier to be careful.
I would call it bad form, not because you double-assign all your objects, but because in my experience it's often bad form to rely on the default copy constructor / assignment operator for a specific set of functionality. Since these are not in the source anywhere, it's hard to tell that the behavior you want depends on their behavior. For instance, what if someone in a year wants to add a vector of strings to your class? You no longer have the plain old datatypes, and it would be very hard for a maintainer to know that they were breaking things.
I think that, nice as DRY is, creating subtle un-specified requirements is much worse from a maintenance point of view. Even repeating yourself, as bad as that is, is less evil.
I think the better way is not to implement a copy constructor if the behaviour is trivial (in your case it appears to be broken: at least assignment and copying should have similar semantics but your code suggests this won't be so - but then I suppose it is a contrived example). Code that is generated for you cannot be wrong.
If you need to implement those methods, most likely the class could do with a fast swap method and thus be able to reuse the copy constructor to implement the assignment operator.
If you for some reason need to provide a default shallow copy constructor, then C++0X has
X(const X&) = default;
But I don't think there is an idiom for weird semantics. In this case using assignment instead of initialization is cheap (since leaving ints uninitialized doesn't cost anything), so you might just as well do it like this.