I am doing the following test:
#include <iostream>
#include <vector>
using namespace std;
class A
{
private:
int i;
public:
A():i(1){cout<<"A constr"<<endl;}
A(const A & a):i(a.i){cout<<"A copy"<<endl;}
virtual ~A(){cout<<"destruct A"<<endl;}
void operator=(const A a){cout<<"A assign"<<endl;}
};
int main()
{
A o1;
A o2;
o2=o1;
}
And the output is:
A constr
A constr
A copy
A assign
destruct A
destruct A
destruct A
It seems that "o2=o1" did a copy first followed by an assignment, and I wonder what's the story behind it. Thanks!
Because you pass by value into your assignment operator:
void operator=(const A a)
You probably meant to pass by reference and you should also return a reference to the assigned-to object:
A& operator=(const A& a) { std::cout << "A assign" << std::endl; return *this; }
You seem to set up you assignment operator to be implemented properly:
T& T::operator= (T value) {
value. swap(*this);
return *this;
}
The argument is passed by copy to the assigment operator and the compiler actually needed to do this copy in your set up. If you had passed a temporary the copy could have been avoided:
o2 = A();
Thus, the implementation above actually has a few interesting properties:
it leverages already written functions: the copy constructor is either generated or written but does the Right Thing and if you want to have an assignment you probably want to have swap() member as well
the assignment is strong exception safe if the swap() operation is non-throwing as it should be. When allocators enter the picture things need to be done slightly different, though
the assignment tries to avoid actual copy operations as the copy during argument passing can be elided in some cases, i.e. the content is just swap()ed into place
Related
I am a beginner in cpp so excuse me for this question.
I was reading that returning const val prevents move semantics.
therefore I dont understand why the following code is compiled and works normally. is it because only temporary object is being created? in what cases the move semantics cannot being done? thank you in advance!
#include <iostream>
using namespace std;
const string foo()
{
return string("hello");
}
int main()
{
string other = std::move(foo());
}
std::move is just a unconditional cast to rvalue. In your case the return value of std::move(foo()) was const std::string&&. And because move constructor does not take const argument, copy constructor was called instead.
struct C {
C() { std::cout << "constructor" << std::endl; }
C(const C& other) { std::cout << "copy constructor" << std::endl; }
C(C&& other) { std::cout << "move constructor" << std::endl; }
};
const C get() {
return C();
}
int main() {
C c(std::move(get()));
return 0;
}
I was reading that returning const val prevents move semantics. therefore I dont understand why the following code is compiled and works normally.
When move semantics are prevented by some mechanism, this doesn't necessarily mean that the code doesn't compile. Often, it compiles happily, but an expected move construction turns out to be a copy instead.
Example: a type has a user provided copy ctor, which disables compiler-generated move ctors. When we think we move-construct, we don't.
struct Test {
Test() = default;
Test(const Test&) {}
};
Test t1;
Test t2{std::move(t1)}; // Copies!
in what cases the move semantics cannot being done?
Coming to your example, something that is const-qualified can't be used to move-construct another object in any meaningful way. Move construction makes sense when resources can be easily transferred, but const-ness prevents that. Example: a type has compiler-generate move and copy constructors, but we can't move-construct from a const instance.
struct Test {
Test() = default;
};
const Test t1;
Test t2{std::move(t1)}; // Copies!
Besides, it doesn't make sense to move something that is returned by a function by value:
string other = std::move(foo());
When foo() returns by value, you can move-construct from it, unless the return type is const. Hence, to enable move-construction of other:
std::string foo();
string other = foo();
std::move doesn't actually move anything. It is just an "rvalue cast". You cast something to rvalue, and the move constructor / move assignment operator does the actual moving if possible. "If possible" part is the key. In your example the return value is already an rvalue, so std::move literally does nothing. You may even get warnings like "nothing is moved". That is because the move constructor of std::string takes an argument of type std::string&& not const std::string&&. Because of that, the copy constructor is called.
Can I simulate move constructor & move assignment operator functionality with copy constructor and assignment operator in C++98 to improve the performance whenever i know copy constructor & copy assignment will be called only for temporary object in the code OR i am inserting needle in my eyes?
I have taken two example's one is normal copy constructor & copy assignment operator and other one simulating move constructor & move assignment operator and pushing 10000 elements in the vector to call copy constructor.
Example(copy.cpp) of normal copy constructor & copy assignment operator
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class MemoryBlock
{
public:
// Simple constructor that initializes the resource.
explicit MemoryBlock(int length)
: _length(length)
, _data(new int[length])
{
}
// Destructor.
~MemoryBlock()
{
if (_data != NULL)
{
// Delete the resource.
delete[] _data;
}
}
//copy constructor.
MemoryBlock(const MemoryBlock& other): _length(other._length)
, _data(new int[other._length])
{
std::copy(other._data, other._data + _length, _data);
}
// copy assignment operator.
MemoryBlock& operator=(MemoryBlock& other)
{
//implementation of copy assignment
}
private:
int _length; // The length of the resource.
int* _data; // The resource.
};
int main()
{
// Create a vector object and add a few elements to it.
vector<MemoryBlock> v;
for(int i=0; i<10000;i++)
v.push_back(MemoryBlock(i));
// Insert a new element into the second position of the vector.
}
Example(move.cpp) of simulated move constructor & move assignment operator functionality with copy constructor & copy assignment operator
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class MemoryBlock
{
public:
// Simple constructor that initializes the resource.
explicit MemoryBlock(int length=0)
: _length(length)
, _data(new int[length])
{
}
// Destructor.
~MemoryBlock()
{
if (_data != NULL)
{
// Delete the resource.
delete[] _data;
}
}
// Move constructor.
MemoryBlock(const MemoryBlock& other)
{
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
(const_cast<MemoryBlock&>(other))._data = NULL;
//other._data=NULL;
}
// Move assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
//Implementation of move constructor
return *this;
}
private:
int _length; // The length of the resource.
int* _data; // The resource.
};
int main()
{
// Create a vector object and add a few elements to it.
vector<MemoryBlock> v;
for(int i=0; i<10000;i++)
v.push_back(MemoryBlock(i));
// Insert a new element into the second position of the vector.
}
I observed performance is improved with some cost:
$ g++ copy.cpp -o copy
$ time ./copy
real 0m0.155s
user 0m0.069s
sys 0m0.085s
$ g++ move.cpp -o move
$ time ./move
real 0m0.023s
user 0m0.013s
sys 0m0.009s
We can observe that performance is increased with some cost.
Has any pitfall to implement move constructor and move assignment
operator simulated functionality in c++98, even I am sure that copy
constructor & assignment only call when temporary objects are
created?
Has there any other way/technique to implement the move constructor
and assignment operator in c++98?
You will not be able to have the language understand R-values in the same way that C++11 and above will, but you can still approximate the behavior of move semantics by creating a custom "R-Value" type to simulate ownership transferring.
The Approach
"Move semantics" is really just destructively editing/stealing the contents from a reference to an object, in a form that is idiomatic. This is contrary to copying from immutable views to an object. The idiomatic approach introduced at the language level in C++11 and above is presented to us as an overload set, using l-values for copies (const T&), and (mutable) r-values for moves (T&&).
Although the language provides deeper hooks in the way that lifetimes are handled with r-value references, we can absolutely simulate the move-semantics in C++98 by creating an rvalue-like type, but it will have a few limitations. All we need is a way to create an overload set that can disambiguate the concept of copying, from the concept of moving.
Overload sets are nothing new to C++, and this is something that can be accomplished by a thin wrapper type that allows disambiguating overloads using tag-based dispatch.
For example:
// A type that pretends to be an r-value reference
template <typename T>
class rvalue {
public:
explicit rvalue(T& ref)
: _ref(&ref)
{
}
T& get() const {
return *_ref;
}
operator T&() const {
return *_ref;
}
private:
T* _ref;
};
// returns something that pretends to be an R-value reference
template <typename T>
rvalue<T> move(T& v)
{
return rvalue<T>(v);
}
We won't be able behave exactly like a reference by accessing members by the . operator, since that functionality does not exist in C++ -- hence having get() to get the reference. But we can signal a means that becomes idiomatic in the codebase to destructively alter types.
The rvalue type can be more creative based on whatever your needs are as well -- I just kept it simple for brevity. It might be worthwhile to add operator-> to at least have a way to directly access members.
I have left out T&& -> const T&& conversion, T&& to U&& conversion (where U is a base of T), and T&& reference collapsing to T&. These things can be introduced by modifying rvalue with implicit conversion operators/constructors (but might require some light-SFINAE). However, I have found this rarely necessary outside of generic programming. For pure/basic "move-semantics", this is effectively sufficient.
Integrating it all together
Integrating this "rvalue" type is as simple as adding an overload for rvalue<T> where T is the type being "moved from". With your example above, it just requires adding a constructor / move assignment operator:
// Move constructor.
MemoryBlock(rvalue<MemoryBlock> other)
: _length(other.get()._length),
_data(other.get()._data)
{
other.get()._data = NULL;
}
MoveBlock& operator=(rvalue<MemoryBlock> other)
{
// same idea
}
This allows you to keep copy constructors idiomatic, and simulate "move" constructors.
The use can now become:
MemoryBlock mb(42);
MemoryBlock other = move(mb); // 'move' constructor -- no copy is performed
Here's a working example on compiler explorer that compares the copy vs move assemblies.
Limitations
No PR-values to rvalue conversion
The one notable limitation of this approach, is that you cannot do PR-value to R-value conversions that would occur in C++11 or above, like:
MemoryBlock makeMemoryBlock(); // Produces a 'PR-value'
...
// Would be a move in C++11 (if not elided), but would be a copy here
MemoryBlock other = makeMemoryBlock();
As far as I am aware, this cannot be replicated without language support.
No auto-generated move-constructors/assignment
Unlike C++11, there will be no auto-generated move constructors or assignment operators -- so this becomes a manual effort for types that you want to add "move" support to.
This is worth pointing out, since copy constructors and assignment operators come for free in some cases, whereas move becomes a manual effort.
An rvalue is not an L-value reference
In C++11, a named R-value reference is an l-value reference. This is why you see code like:
void accept(T&& x)
{
pass_to_something_else(std::move(x));
}
This named r-value to l-value conversion cannot be modeled without compiler support. This means that an rvalue reference will always behave like an R-value reference. E.g.:
void accept(rvalue<T> x)
{
pass_to_something_else(x); // still behaves like a 'move'
}
Conclusion
So in short, you won't be able to have full language support for things like PR-values. But you can, at least, implement a means of allowing efficient moving of the contents from one type to another with a "best-effort" attempt. If this gets adopted unanimously in a codebase, it can become just as idiomatic as proper move-semantics in C++11 and above.
In my opinion, this "best-effort" is worth it despite the limitations listed above, since you can still transfer ownership more efficiently in an idiomatic manner.
Note: I do not recommend overloading both T& and const T& to attempt "move-semantics". The big issue here is that it can unintentionally become destructive with simple code, like:
SomeType x; // not-const!
SomeType y = x; // x was moved?
This can cause buggy behavior in code, and is not easily visible. Using a wrapper approach at least makes this destruction much more explicit
In an already existing class of a project I am working on I encountered some strange piece of code: The assignment operator calls the copy constructor.
I added some code and now the assignment operator seems to cause trouble.
It is working fine though if I just use the assignment operator generated by the compiler instead. So I found a solution, but I'm still curious to find out the reason why this isn't working.
Since the original code is thousands of lines I created a simpler example for you to look at.
#include <iostream>
#include <vector>
class Example {
private:
int pValue;
public:
Example(int iValue=0)
{
pValue = iValue;
}
Example(const Example &eSource)
{
pValue = eSource.pValue;
}
Example operator= (const Example &eSource)
{
Example tmp(eSource);
return tmp;
}
int getValue()
{
return pValue;
}
};
int main ()
{
std::vector<Example> myvector;
for (int i=1; i<=8; i++) myvector.push_back(Example(i));
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i].getValue();
std::cout << '\n';
myvector.erase (myvector.begin(),myvector.begin()+3);
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i].getValue();
std::cout << '\n';
return 0;
}
The output is
myvector contains: 1 2 3 4 5
but it should be (an in fact is, if I just use the compiler-generated assignment operator)
myvector contains: 4 5 6 7 8
Your operator= does not do what everyone (including the standard library) thinks it should be doing. It doesn't modify *this at all - it just creates a new copy and returns it.
It's normal to re-use the copy constructor in the copy assignment operator using the copy-and-swap idiom:
Example& operator= (Example eSource)
{
swap(eSource);
return *this;
}
Notice how the operator takes its parameter by value. This means the copy-constructor will be called to construct the parameter, and you can then just swap with that copy, effectively assigning to *this.
Also note that it's expected from operator= to return by reference; when overloading operators, always follow the expected conventions. Even more, the standard actually requires the assignment operator of a CopyAssignable or MoveAssignable type to return a non-const reference (C++11 [moveassignable] and [copyassignable]); so to correctly use the class with the standard library, it has to comply.
Of course, it requires you to implement a swap() function in your class:
void swap(Example &other)
{
using std::swap;
swap(pValue, other.pValue);
}
The function should not raise exceptions (thanks #JamesKanze for mentioning this), no to compromise the exception safety of the operator=.
Also note that you should use the compiler-generated default constructors and assignment operators whenever you can; they can never get out of sync with the class's contents. In your case, there's no reason to provide the custom ones (but I assume the class is a simplified version for posting here).
The assignment operator you found is not correct. All it does is make a copy of eSource, but it's supposed to modify the object on which it is called.
The compiler-generated assignment operator for the class is equivalent to:
Example &operator= (const Example &eSource)
{
pValue = eSource.pValue;
return *this;
}
For this class there's no point implementing operator=, since the compiler-generated version basically cannot be improved on. But if you do implement it, that's the behaviour you want even if you write it differently.
[Alf will say return void, most C++ programmers will say return a reference. Regardless of what you return, the vital behaviour is an assignment to pValue of the value from eSource.pValue. Because that's what the copy assignment operator does: copy from the source to the destination.]
First of all, operator=() should return a reference:
Example& operator=(const Example& eSource)
{
pValue = eSource.pValue;
return *this;
}
Mind that your version returns a copy of tmp so in fact it performs two copies.
Second of all, in your class there's no need to even define custom assignment operator or copy constructor. The ones generated by compiler should be fine.
And third of all, you might be interested in copy-and-swap idiom: What is the copy-and-swap idiom?
Probably the most frequent correct implementation of operator=
will use the copy constructor; you don’t want to write the same
code twice. It will do something like:
Example& Example::operator=( Example const& other )
{
Example tmp( other );
swap( tmp );
return *this;
}
The key here is having a swap member function which swaps the
internal representation, while guaranteeing not to throw.
Just creating a temporary using the copy constructor is not
enough. And a correctly written assignment operator will always
return a reference, and will return *this.
I want to redirect my copy operator to my copy constructor. Where in the latter I implement the proper logic for copying/contructing a new class based on the old avaialble class.
However, how is the proper way to do this? I "think" this one is maybe leaking memory, but I don't know how to do it without passing a pointer:
MyClass& MyClass::operator=(const MyClass& a) {
MyClass* b = new MyClass(a);
return *b;
}
Is this OK? If is not, what would be the proper way? Should I change the body of the method or the prototype?
Thank you.
No, an operator= should set the current object attributes to be the same as the object assigned. Your method assigns a new object on the heap, returns it as a reference (essentially leaking it) and leaves the object the operator was called on completely unchanged.
You should implement a method called, for example, CopyFrom(), which assigns all the object's attributes to match those of the passed in object (deep copying any heap allocated pointers whose lifetime is managed by MyClass) and then call THAT from both your copy constructor and your operator=.
class MyClass
{
public:
MyClass( const MyClass& in )
{
CopyFrom( in );
}
MyClass& operator=( const MyClass& in )
{
CopyFrom( in );
return *this;
}
private:
void CopyFrom( const MyClass& in )
{
... copies in's attributes into self.
}
};
As a rule, a copy assignment operator should never create a copy. Rather, it should copy data into the existing object that it's called on (the left-hand side of the assignment). For example:
class MyClass
{
public:
MyClass & operator = (const MyClass & RHS)
{
// Copy data from RHS into 'this'
m_value = RHS.m_value;
return *this;
}
private:
int m_value;
};
In this case, defining your own copy constructor isn't necessary because the default (compiler-provided) one would work fine. It's just an illustration though.
Unfortunately you can't invoke the copy constructor on the existing object. The copy-swap pattern is an alternative, but it can be less efficient.
Unless you're storing pointers inside of MyClass, the correct copy assignment operator is the default-generated one. If, however you need to implement one, you can write it in terms of your copy-constructor via the copy-swap idiom:
MyClass& MyClass::operator = (MyClass const& a) {
MyClass temp(a); // Call the copy constructor
using std::swap;
swap(temp, *this);
return *this;
}
The reason for the using std::swap is to enable argument-dependent lookup. If you define your own swap function for MyClass, it will be called. Else std::swap will be used as a fallback. (EDIT: You do in fact need to implement a custom swap in this case, or else you will get infinite recursion. std::swap will use the assignment operator, which will call std::swap, which will call the...)
The reason that this idiom is well-liked is because std::swap is a no-throw function. If your copy-constructor were to throw an exception, then your object you're assigning to is still in a valid state.
The "proper way" is to implement the assignment operator like an assignment operator: modify the contents of the object on which the operator is being called and return a reference to it.
Your current implementation will result in a memory leak, AND doesn't do any assignment (which is the main point of the assignment operator).
If you only want to write the assignment code once, and your class doesn't allocate memory in the constructor, you could do this:
MyClass::MyClass(const MyClass& a) {
*this = a;
}
MyClass& MyClass::operator=(const MyClass& a) {
if (&a == this)
return *this;
// Do assignment
return *this;
}
But I wouldn't recommend it.
Your code is totally wrong (sorry)! The assignment operator does
not assign anything, but allocates a pointer to a MyClass object,
creating a memory leak. My advice: avoid pointers or use some
smart pointer (shared_ptr, unique_ptr), but that is just a side note.
Maybe this is helpful:
#include <iostream>
#include <limits>
class X
{
public:
X(std::size_t n)
: m_size(n), m_data(new int[n])
{
std::cout << "Construct" << std::endl;
}
~X()
{
std::cout << "Destruct" << std::endl;
delete [] m_data;
}
// Exception safe assignment.
// Note: I am passing by value to enable copy elision and
// move semantics.
X& operator = (X x) {
std::cout << "Assign" << std::endl;
x.swap(*this);
return *this;
}
void swap(X& x) {
std::swap(m_size, x.m_size);
std::swap(m_data, x.m_data);
}
std::size_t size() const { return m_size; }
private:
std::size_t m_size;
int* m_data;
};
int main()
{
X x(1);
try {
x = X(2);
// To provoke an exception:
std::size_t n = std::numeric_limits<std::size_t>::max();
x = X(n);
}
catch(...) {
std::cout << "Exception" << std::endl;
}
std::cout << "Size: " << x.size() << std::endl;
return 0;
}
If you absolutely want to implement the assignment operator by copy constructor, use the following:
MyClass& MyClass::operator=(const MyClass& o)
{
this->~MyClass(); // destroy current object
new(this) MyClass(o); // use the copy constructor
return *this;
}
I cannot think of any situation in which this would be the best thing to do (other answers describe ways of implementation that are better in some situations).
Maybe (just trying to make things up here) if MyClass contains hundreds of int/float fields, and several dynamically-allocated pointers?
Duplicating them in constructor and assignment operator is too tedious and error-prone
Having a copying function that both constructor and assignment operator call - not ideal, because pointers have to be set to NULL first
The code above - will work with no additional effort!
However, having bare (non-smart) pointers in your class is discouraged. If you have such a class, then you have far worse problems than non-working assignment operator - you have to refactor first, and the problem will go away, together with all other bugs.
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?