I'm struggling to basically understand how the assignment operator works, or perhaps I haven't defined the right one, or I've come upon some C++ gotcha; not sure.
I have a class like this:
class A
{
public:
A();
A(const A &rhs);
//some other ctors
//and functions here
A& operator=(A rhs);
A create_half();
double* m_data;
};
Here is the body of create_half; it appears to be relevant to the situation
A A::create_half(){
//construct a new A
A mya;
//do stuff to mya here,
//and then return the new A
return mya;
}
Here is the copy constructor, at least a shortened version of it. This is the code that is skipped due to standard -- at least if this is what's truly going on.
A::A(const A& rhs):
m_data(new double[1])
{
*m_data = *(rhs.m_data);
//or something similar, essentially
//a deep copy
}
Then in the code I try to write something like this
A a_instance;
//do stuff to a_instance
//this doesn't call any = operator or
//copy c-tor I try to define, why?
A b_instance = A.create_half();
//but this works fine
//the = operator I have def'd is called
A b_instance;
b_instance = A.create_half();
The line that doesn't work as I want it to seems to do a default copy (ie, b_instance.m_data is set to a_instance.m_data) even though neither my copy c-tor now my assignment operator contain this operation.
For the problematic line, I'm not able to step into the assignment operator; that's why I think I have the wrong signature, or something similar.
Any ideas?
EDIT: Sorry for misleading you guys -- no class B exists.
EDIT AGAIN: I've accepted an answer, and I've added some detail to the snippets above to try to elucidate what is really going on.
Since the instance of A created by create_half() isn't bound to a reference, the standard allows elision of the copy constructor, which, critically, allocates new memory for b_instance, and does the deep copy.
That seems pretty bold, to me! I don't think I'll see an answer, since this edit is sometime after the initial posting, but what would be a better design pattern here?
This
A a_instance();
is a function declaration that has no parameters and has return type A.
This statement
B b_instance = A.create_half();
syntactically is invalid. At least it should be written as
B b_instance = A().create_half();
In this statement an object of type B is created from an object of type A. I can say nothing whether this statement is correct because you did not show the class B definition.
This statement is also invalid
b_instance = A.create_half();
There must be
b_instance = A().create_half();
There is used indeed an assignment operator (maybe even the copy assignment operator provided that class B has a conversion constructor or class A has a conversion function) but for class B. You did not show how you defined class B. So again I can say nothing what is wrong with this assignment operator.
As for the signature of the copy assignment operator of class A
A& operator=(A rhs);
then it is valid though it would be better if you declared it as
A& operator=( const A &rhs );
EDIT: As you changed your original post then I need to append my message relative to your question in the following code snippet
//this doesn't call any = operator or
//copy c-tor I try to define, why?
A b_instance = A.create_half();
According to the C++ Standard
31 When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects. In such cases, the implementation treats the
source and target of the omitted copy/move operation as simply two
different ways of referring to the same object, and the destruction of
that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.
One of the "certin criteria" is
— when a temporary class object that has not been bound to a reference
(12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move
Related
In the cppreference it is said that
The copy/move constructors need not be present or accessible in a
return statement, when the operand is a prvalue of the same class type
(ignoring cv-qualification) as the function return type:
T f() {
return T();
}
f(); // only one call to default constructor of T
Am I understanding this right and in order to make the above example work, it is inevitable to have at least one copy or move constructor?
What I tried is the following:
class Player
{
//...
public:
Player();
Player(std::string name);
Player& operator=(const Player&) = delete;
Player& operator=(Player&& p) = delete;
Player(const Player& origin) = delete;
Player(Player&& p) = delete;
Player getEmptyPlayer() const {
return Player("Name");
}
}
//in main:
Player p1("It's Me");
Player p2 = p1.getEmptyPlayer();
Why is this code compiling and working? To my understanding actually the method getEmptyPlayer() would need to call the copy constructor for copying the object when returning. Due to efficiency improvements by the compiler this is not the case but the copy elision (NRVO) comes to turn and directly constructs the object where it should be and does not make a copy. Nonetheless, the cppreference (see above) says that an accessible or present copy/move constructor needs to exist. So what is happening here?
Thank you in advance!
Copy elision (NRVO) with deleted copy and move constructors possible?
No.
If there is a copy (or move), then the type must be copyable (or movable). Just because the copy (or move) could be optimised away does not make such program well-formed.
Why is this code compiling and working?
Because there is no copy (nor move) involved (since C++17).
To my understanding actually the method getEmptyPlayer() would need to call the copy constructor for copying the object when returning.
It does not since C++17.
It used to be needed before C++17 (and the call could be elided).
The question absolute nonsense. The cite says "The copy/move constructors need not be present [...]".
I am terribly sorry for not reading correctly.
I am struggling to understand implicit move operations when a class has a member whose move operations were not defined:
int main() {
struct A // no move: move = copy
{
A() = default;
A(const A&) {
cout << "A'copy-ctor\n";
};
A& operator=(const A&) {
cout << "A'copy-assign\n";
return *this;
}
};
struct B
{
B() = default;
A a; // does this make B non-moveable?
unique_ptr<int> upi;
// B(B&&) noexcept = default;
// B& operator=(B&&)noexcept = default;
};
A a;
A a2 = std::move(a); // ok use copy ctor instead of move one
a2 = std::move(a); // ok use copy assignment instead of move one
B b;
B b2 = std::move(b); // why this works?
b = std::move(b2); // and this works?
// b = b2; // error: copy deleted because of non-copyable member upi
cout << "\nDone!\n";
}
So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.
Until here it is OK if i am correct. But B has a non-copy-able object upi which is a unique_ptr thus the copy operations are defined as deleted functions so we cannot copy objects of this class. But this class has a non-move-able object a thus i think that this class (B) is neither copy-able nor move-able. But why the initialization of b2 and the assignment of b works fine? What happens exactly?
B b2 = std::move(b); // ok?!
Why the line above invokes the copy constructor of class A and does it invoke move constructor of B?
Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!
Can anyone help me what happens exactly? I have googled and read in cppreference and many websites before posting the question here.
The output:
A'copy-ctor
A'copy-assign
A'copy-ctor
A'copy-assign
Done!
Keep in mind what it means to "move" data in C++ (assuming we follow the usual conventions). If you move object x to object y, then y receives all the data that was in x and x is... well, we don't care what x is as long as it is still valid for destruction. Often we think of x as losing all of its data, but that is not required. All that is required is that x is valid. If x ends up with the same data as y, we don't care.
Copying x to y causes y to receive all the data that was in x, and x is left in a valid state (assuming the copy operation follows conventions and is not buggy). Thus, copying counts as moving. The reason for defining move operations in addition to copy operations is not to permit something new, but to permit greater efficiency in some cases. Anything that can be copied can be moved unless you take steps to prevent moves.
So what I see is A is a non-moveable class because of the definition of its copy control operations so it can only be copied and any attempt to move an object of this class, the corresponding copy operation is used instead.
What I see is that A is a moveable class (despite the lack of move constructor and move assignment), because of the definition of its copy control operations. Any attempt to move an object of this class will fall back on the corresponding copy operation. If you want a class to be copyable but not movable, you need to delete the move operations, while retaining the copy ones. (Try it. Add A(A&&) = delete; to your definition of A.)
The B class has one member that can be moved or copied, and one member that can be moved but not copied. So B itself can be moved but not copied. When B is moved, the unique_ptr member will be moved as you expect, and the A member will be copied (the fallback for moving objects of type A).
Things get more worse for me: if I uncomment the lines of move operations in B, the initialization above will not compile complaining about referencing a deleted funtion, the same thing for the assignment!
Read the error message more closely. When I replicated this result, the "use of deleted function" error was followed by a note providing more details: the move constructor was deleted because "its exception-specification does not match the implicit exception-specification". Removing the noexcept keywords allowed the code to compile (using gcc 9.2 and 6.1).
Alternatively, you could add noexcept to the copy constructor and copy assignment of A (keeping noexcept on the move operations of B). This is one way to demonstrate that the default move operations of B use the copy operations of A.
Here is a summary of #JaMiT's excellent answer:
Class A is moveable via it's copy-constructor and copy-assignment operator, even though class A is not MoveConstructible and is not MoveAssignable. See the notes on cppreference.com's pages for MoveConstructible and MoveAssignable.
And thus class B is also moveable.
The language allows you to prevent moveability for class A by explicitly =delete'ing the move-constructor and move-assignment, even though class A is still copyable.
Is there a practical reason to have a copyable but not-moveable class? Someone asked just this question several years ago here. The answers and comments struggled to find any practical reason to want a copyable but not-moveable class.
std::move does not force object to be copied. It just returns &&-reference (which allows compiler to use move ctor/assign operator).
In cases 1,2 object is copied.
In 3,4 cases (i think) object is moved. But A is still copied because it cannot be moved.
Environment: VS2015 Update 3, 64 bit (debug|release) compile
In the code below, if I uncomment the Maybe(X&) = delete line, I get the warning mentioned in the code below and in the title of the question.
Now, I am aware, that there are certain rules in C++(11?), which might render
the explicit deletion of that constructor obsolete. Only, even after searching
the web for a while, I could not find a definite rule, which would confirm, that
if I only delete Maybe(const X&) = delete, the compiler will not auto generate the other copy constructor.
So my question is first and foremost:
Can anyone point me to spot in the C++ specification, which clearly defines the
rules for auto generation of copy constructors? Alternatively, some less official
easy to remember rule of thumb on how to be certain of what will happen would also be welcome.
template <class X>
class Maybe
{
X *m_just;
public:
explicit Maybe(const X& x)
: m_just(new X(x))
{}
Maybe()
: m_just(nullptr)
{}
Maybe(const Maybe<X>&& other)
: m_just(other.m_just)
{
other.m_just = nullptr;
}
Maybe(const Maybe<X>&) = delete;
// If line below is uncommented, this produces the warning:
// warning C4521: 'Maybe<Int32>': multiple copy constructors specified
// Maybe(Maybe<X>&) = delete;
~Maybe()
{
delete m_just;
m_just = nullptr;
}
// ... more members and code which are not related to question
// ...
};
Please do not comment on the whole idea of that class. It is just private tinkering in my private lab... ;)
The compiler will only generate one of Maybe(Maybe<X>&) and Maybe(const Maybe<X>&).
The conditions for the choice are listed in section 12.8, paragraph 8:
The implicitly-declared copy constructor for a class X will have the
form X::X(const X&) if
each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and
for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose
first parameter is of type const M& or const volatile M&
Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
Or more informally, the parameter will be const if and only if everything that needs to be copied can be "const-copied".
Deleting one will not cause the other to be generated.
Since you have no base class and your only member is a pointer, the generated constructor will be of the const variety and you can leave out the line causing an error.
My C++ is a little bit rusty so this might be wrong, but I don't think you should even have a non-const copy constructor (except for the move constructor naturally).
If a = b changes b you're going to surprise some people. Perhaps it's legitimate for the copy constructor to change the source in a way that's not externally visible, but in that case I think you're better off using const_cast inside the const copy constructor.
The error I don't think is about delete, it's about defining multiple copy constructors, which isn't allowed. I suspect even allowing the non-const copy constructor is a bit of a wart in the language.
other.m_just = nullptr;
This line should not compile. Something must be wrong with your compiler, or your code example doesn't exactly reflect what you're compiling.
Anyway, the move constructor should take a non-const rvalue reference.
The commented line is simply not necessary. Just leave it out. If you define the copy constructor (even as deleted), no other form of the copy constructor is defined by the compiler, ever, so if you leave the line out, there simply is no constructor that takes a non-const reference to Self, meaning that overload resolution will just pick the const reference copy constructor, which is deleted anyway.
Unit now I've never needed to overload the assignment parameter or write a Copy Constructor
(at least, it seems I never had to, because I never had problems)
as far as I know the Assignment Operator must be overloaded when I want to copy a Object and its values to another Object after initialization (Same of CopyConstructor, but when its initialized).
Object a;
Object b;
// do something with a
b = a; //for this line, the Assignment Parameter must be overloaded (nearly never do such things)
Object c = a; // needs a Copy constructor
But If I would write it like this:
Object a;
Object* b;
b = &a; //I think I won't need one here since b actually points to Object a
Object* c = a; // I think same here
But what should I do, if I copy a object outside of its own class/parent class.
class MyOtherClass{Object obj..........}
void MyOtherClass::SetObject(Object obj)
{
this->obj = obj; // Assignment operator overloading needed in class Object?
}
do I have to overload the Assignment Operator in the Object class?
What if I do following:
void MyOtherClass::SetObject(Object &obj)
{
this->obj = obj; // Assignment operator overloading needed in class Object?
}
And the final question, does this also include Enums?(I think yes)
void MyOtherClass::SetObject(ENumClass Eobj)
.
.
For the question of whether or not to implement an assignment operator, the answer is - it depends.
Remember that the compiler will generate its own default assignment operator if you do not provide one yourself (and in C++11, if you do not explicitly disable the default operator). The default operator performs an assignment of each individual class member using their own assignment operators.
So as long as your class members are assignment-safe in their own right, then the default operator is sufficient and you do not need to provide your own operator. You only need to provide your own operator when you need to perform custom actions, like deep-copying dynamically allocated members, debug logging, input validation, etc.
Why do you use a separate method SetObject() which duplicates operator=() semantic?
So that correctly assign a base class to derived you can overload operator=() in the derived object this way
DerivedObj& DerivedObj::operator=(const BaseObj& rhs){
// call parent operator= in functional form
BaseObj::operator=(rhs);
return *this;
}
Of course you should implement
BaseObj& BaseObj::operator=(const BaseObj& rhs)
However if you need assign base class to derived I would not say it's a good design
It is legal to write an assignment operator that assigns from a base type and it is also legal to write an assignment operator that assigns from a concrete object of the same type. However, even though both are legal, it is generally better design to implement assignment from an object of the same type or from other concrete types for which such an assignment actually makes sense and not from a general type.
That being said, it does vary from application to application. To be a little more specific, consider:
Shape
/ | \
Triangle Quadrilateral Circle
... it probably would not make sense to have an assignment operator in Circle that could assign from any Shape. What would that method even do? These different implementations have nothing in common.
Now, alternatively, consider:
Point2D
/ \
CartesianCoord PolarCoord
... in an application like this, it might make sense to provide an assignment operator that accepts a general Point2D in order to facilitate conversions between the two types. In this case, all implementations represent the same information and can be converted non-destructively between representations. As a result, allowing a more general conversion would make sense in this context.
So, really, it's all about what makes sense conceptually. If you can assign to/from an object in a way that does not destroy information, it is sane; if your assignment is destructive, it's likely a poor design.
As for the parameters, one typically uses "const T&" (where "T" is the type from which you are assigning) or "T&&" in the case of destructive assignment. The return type of the assignment should be a reference to an object of the same type as the current object. In other words:
class CartesianCoord : public Point2D {
public:
// ...
CartesianCoord& operator=(const Point2D&);
};
Or
class CartesianCoord : public Point2D {
public:
// ...
CartesianCoord& operator=(const Point2D&);
CartesianCoord& operator=(CartesianCoord&&); // fast destructive assignment
};
Or
class CartesianCoord : public Point2D {
public:
// ...
CartesianCoord& operator=(const CartesianCoord&); // if conversion disallowed
CartesianCoord& operator=(CartesianCoord&&); // fast destructive assignment
};
Is it necessary to have a copy constructor, destructor and operator= in a class that have only static data member, no pointer
class myClass{
int dm;
public:
myClass(){
dm = 1;
}
~myClass(){ } // Is this line usefull ?
myClass(const myClass& myObj){ // and that operator?
this->dm = myObj.dm;
}
myClass& operator=(const myClass& myObj){ // and that one?
if(this != &myObj){
this->dm = myObj.dm;
}
return *this;
}
};
I read that the compiler build one for us, so it is better to not have one (when we add a data member we have to update the operators)
If you can't think of anything for the destructor to do, you almost certainly don't need to define it, with the exception of a virtual destructor in a base class. And if you don't need a destructor, you almost certainly don't need to define the copy constructor and assignment operator, and should not do so, as it's easy to get them wrong.
No. The compiler will automatically copy-construct, destruct, and assign all data members automatically. This means that if you have a class where they're all provided, you don't need to write these. I think it's better design to write operators on a per-member basis anyway.
The compiler would provide those itself. By default the copy constructor / assignment operator would copy / assign all members (and base parts of the object if the class is derived), and the destructor does nothing (invokes the destructor of all members / bases, but this is something you can't change even if you provide your own - that happens after the body of the user-defined destructor is exited).
So in this case it is completely unnecessary. All you can achieve is to introduce bugs in the code (e.g you add another member, but forget to update the copy constructor to copy that as well).
myClass& operator=(const myClass& myObj){ // and that one?
if(this != &myObj){
this->dm = myObj.dm;
}
return *this;
}
I don't think you should take "check for self-assignment" dogmatically. Does anything bad happen when you assign an int to itself (even indirectly)?
int a = 10;
int& ref = a;
a = ref;
Avoiding self-assignment is only relevant if the assignment operator first destroys the resources currently held and then creates new ones from the object on the right-hand side. Then it would be a disaster to find out that you have actually indirectly destroyed the right-hand object too.
But even in that case, it would be better to first create the copy of the right-hand side and then destroy the contents of the left-hand side, in which case self-assignment is no problem (except potentially inefficient). For a good way to implement this, Google for copy and swap idiom.