While learning the concept of "copying members", the book gives the following statement.
In addition, a default assignment cannot be generated if a nonstatic member is a reference, a const,or a user-defined type without a copy assignment.
I do not quite understand what does this statement really want to deliver? Or which kind of scenario does this statement refer to? Thanks.
This statement has to do with the compiler automatically generating the default assignment operator function for a class you write (i.e. user-defined type). The default assignment works by copying all the members over to a new instance. This statement covers three cases where a default assignment would not be able to be generated:
1) When a member is a reference (i.e. refers to an instance of a variable, like a pointer)
class Foop {
int& reference;
};
2) When a member variable is constant
class Foople {
const int someConst;
};
3) When some other class does not have a copy-constructor and you have a member variable of that type, obviously it cannot be copied using the default method (which uses copy-constructors)
class Uncopyable {
private:
Uncopyable(Uncopyable const& other);
};
class Fleep {
Uncopyable uncopyable;
};
In these cases, you would need to write your own assignment operator (or possibly do without).
If you have a member in your class which is not static (shared between all instances of class), and is either
a reference (high level pointer)
a constant
a user-defined type with dynamic data (the same as the class we're talking about)
The default = operator and copy constructor is no longer valid and you should write manual versions of those.
class ClassA
{
int& _myReferenceMember;
const int _myConstant;
ClassB _objWhereClassBHasNoCopyConstructor;
}
Above are examples of the three cases you described. And as you quoted, you must write a custom copy constructor (if you want a copy constructor at all) in such a case, or change your member variables.
It refers to the distinction between:
class A { int a; };
and
class B { int& a; };
For class A, the compiler will generate an implicit assignment operator (=), but in the case of B, it cannot. This is because references in C++ don't have pointer semantics. i.e. you cannot change what a reference point to after it is constructed, hence, the implicit copy constructor would not be able to copy that member. The same thing goes for const members (which are explicitly marked as being immutable) and members which don't have a assignment operators (implicit or explicit).
The default assignment operator for A would essentially do this:
class A
{
A& operator=(A const& a_) { a = a_.a; }
int a;
};
Related
Say I have:
class Foo {
union {
double a;
std::string b;
};
};
What will the default constructor generated by the compiler do? My understanding of the C++ standard is that primitives are not initialized but objects are. So what happens here?
The default constructor (as well as the copy constructor, assignment operator and destructor) for the union is implicitly declared deleted, and so is the constructor of Foo. You'll see this once you try to actually create an instance of Foo.
My problem is very easily explained by this example :
http://pastebin.com/VDBE3miY
class Vector3
{
float _x;
float _y;
float _z;
public :
/// constructors and stuff
};
class Point : public Vector3
{
// some BS
Point(float _x):Vector3(float _x)
{}
};
main()
{
Point aPoint(3);
Point anotherPoint(4);
// WHY DOES THIS WORK and copy _x,_y & _z properly
aPoint = anotherPoint;
}
Basically, I am at a loss to understand why the = for the derived class can copy _x, _y and _z, even though it shouldn't have access to them since they are private.
aPoint = anotherPoint;
This line triggers a call of Point::operator= (the assignment operator), which exists because the compiler generates a default implementation. This default implementation performs assignment operations for all members of the class, as well as calling Vector3::operator=, the assignment operator of the base class. This, in turn, is a member function of Vector3 and therefore has access to all private members, which it makes copies of.
(EDIT) A quote from the C++11 Standard to back this answer:
(§12.8/28) The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy-/move assignment of its subobjects. The direct base classes of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition. Let x be either the parameter of the function or, for the move operator, an xvalue referring to the parameter. Each subobject is assigned in the manner appropriate to its type:
— if the subobject is of class type, as if by a call to operator= with the subobject as the object expression
and the corresponding subobject of x as a single function argument (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
— if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
— if the subobject is of scalar type, the built-in assignment operator is used.
Some of the other (now partly deleted) answers mentioned the idea of a bitwise copy performed by the assignment operation. There is some truth in this: If your class or struct defines a POD (plain old data) type, it is for all practical purposes identical to a C struct. In that case, it can be copied by performing memcpy, therefore you can think of the assignment operation as being basically equivalent to a bitwise copy. But the reason why this is a valid way of thinking about it is §12.8/28 above, and that applies to non-POD types as well.
Also note that from your code it is not necessarily clear that your data type is POD. You mentioned constructors and stuff in the base class: If this involves non-trivial copy constructors, assignment operators or possibly virtual functions, then your data type is no longer POD.
About the question in the comment: In order to call the base-class assignment operator from within the derived-class implementation, just call it:
struct Base
{
};
struct Derived : Base
{
Derived &operator=(const Derived &other)
{ Base::operator=(other); return *this; }
};
Because the default copy operator of Vector3 is invoked (shallow copy).
Because the compiler generate assignment operator(s)
Point& operator=(Point const& rhs)
{
Vector3::operator=(rhs);
return *this;
}
Vector3& operator=(Vector3 const& rhs)
{
// A class is a friend of irself.
// So an object of Type A can read any other object of type A
_x = rhs._x;
_y = rhs._y;
_z = rhs._z;
return *this;
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What’s the point in defaulting functions in C++11?
C++11 introduced defaulted methods (e.g. void myMethod() = default;).
What does it do to methods (how do methods behave after being defaulted). How do I use them properly (what are its uses)?
There are a number of class members that are considered "special member functions" by the C++ standard. These are:
The default constructor (a constructor that can be called with no parameters).
The copy constructor (a constructor that can be called with one parameter that is the object type as an lvalue reference).
The copy assignment operator (an operator= overload that can be called with one parameter that is the object type as either an lvalue reference or a value).
The move constructor (a constructor that can be called with one parameter that is the object type as an rvalue reference).
The move assignment operator (an operator= overload that can be called with one parameter that is the object type as either an rvalue reference or a value).
The destructor.
These member functions are special in that the language does special things with them on types. Another thing that makes them special is that the compiler can provide definitions for them if you do not. These are the only functions that you can use the = default; syntax on.
The issue is that the compiler will only provide a definition under certain conditions. That is, there are conditions under which a definition will not be provided.
I won't go over the entire list, but one example is what others have mentioned. If you provide a constructor for a type that is not a special constructor (ie: not one of the constructors mentioned above), a default constructor will not be automatically generated. Therefore, this type:
struct NoDefault
{
NoDefault(float f);
};
NoDefault cannot be default constructed. Therefore, it cannot be used in any context where default construction is needed. You can't do NoDefault() to create a temporary. You can't create arrays of NoDefault (since those are default constructed). You cannot create a std::vector<NoDefault> and call the sizing constructor without providing a value to copy from, or any other operation that requires that the type be DefaultConstructible.
However, you could do this:
struct UserDefault
{
UserDefault() {}
UserDefault(float f);
};
That would fix everything, right?
WRONG!
That is not the same thing as this:
struct StdDefault
{
StdDefault() = default;
StdDefault(float f);
};
Why? Because StdDefault is a trivial type. What does that mean? I won't explain the whole thing, but go here for the details. Making types trivial is often a useful feature to have, when you can do it.
One of the requirements of a trivial type is that it does not have a user-provided default constructor. UserDefault has a provided one, even though it does the exact same thing as the compiler-generated one would. Therefore, UserDefault is not trivial. StdDefault is a trivial type because = default syntax means that the compiler will generate it. So it's not user-provided.
The = default syntax tells the compiler, "generate this function anyway, even if you normally wouldn't." This is important to ensure that a class is a trivial type, since you cannot actually implement special members the way the compiler can. It allows you to force the compiler to generate the function even when it wouldn't.
This is very useful in many circumstances. For example:
class UniqueThing
{
public:
UniqueThing() : m_ptr(new SomeType()) {}
UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
UniqueThing &operator =(const UniqueThing &ptr)
{
m_ptr.reset(new SomeType(*ptr)); return *this;
}
private:
std::unique_ptr<SomeType> m_ptr;
};
We must write every one of these functions to make the class copy the contents of the unique_ptr; there's no way to avoid that. But we also want this to be moveable, and the move constructor/assignment operators won't automatically be generated for us. It'd silly to re-implement them (and error-prone if we add more members), so we can use the = default syntax:
class UniqueThing
{
public:
UniqueThing() : m_ptr(new SomeType()) {}
UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
UniqueThing(UniqueThing &&ptr) = default;
UniqueThing &operator =(const UniqueThing &ptr)
{
m_ptr.reset(new SomeType(*ptr)); return *this;
}
UniqueThing &operator =(UniqueThing &&ptr) = default;
private:
std::unique_ptr<SomeType> m_ptr;
};
Actually, default can only apply to special methods - i.e. constructors, destructors, assignment operator:
8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]
1 A function definition of the form:
attribute-specifier-seqopt decl-specifier-seqopt declarator = default ;
is called an explicitly-defaulted definition. A function that is explicitly defaulted shall
— be a special member function,
— have the same declared function type (except for possibly differing ref-qualifiers and except that in
the case of a copy constructor or copy assignment operator, the parameter type may be “reference to
non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared,
and
— not have default arguments.
They behave the same as if the compiler generated them and are useful for cases like:
class Foo{
Foo(int) {}
};
here, the default constructor doesn't exist, so Foo f; wouldn't be valid. However, you can tell the compiler that you want a default constructor without the need to implement it yourself:
class Foo{
Foo() = default;
Foo(int) {}
};
EDIT: Pointed out by #Nicol Bolas in the comments, this truly doesn't explain why you'd prefer this over Foo() {}. What I'm guessing is: if you have a class w/ implementations separated in an implementation file, your class definition (if you don't use =default) would look like:
class Foo{
Foo();
Foo(int);
};
What I'm assuming is that the =default is meant to provide an idiomatic way of telling you "we're not doing anything special in the constructor". With the above class definition, the default constructor could very well not value-initialize the class members. With the =default you have that guarantee just by looking at the header.
Using = default on a constructor or copy-constructor means that the compiler will generate that function for you during compile-time. This happens normally when you exclude the respective functions from your class/struct. And in the case where you define your own constructor, this new syntax allows you to explicitly tell the compiler to default-construct the constructor where it normally won't. Take for instance:
struct S {
};
int main() {
S s1; // 1
S s2(s1); // 2
}
The compiler will generate both a copy-constructor and a default-constructor, thus allowing us to instantiate an S object like we did (1) and copy s2 into s1 (2). But if we define our own constructor, we find that we are unable to instantiate in the same way:
struct S {
S(int);
};
int main() {
S s; // illegal
S s(5); // legal
}
This fails because S has been given a custom-defined constructor, and the compiler won't generate the default one. But if we still want the compiler to give us one, we can explicitly convey this using default:
struct S {
S(int);
S() = default;
S(const S &) = default;
};
int main() {
S s; // legal
S s(5); // legal
S s2(s); // legal
}
Note, however, that only certain functions with certain function-signatures can be overloaded. It follows that this will fail because it is neither a copy-constructor nor a default-constructor or a move or assignment operator:
struct S {
S(int) = default; // error
};
We are also able to explicitly define the assignment operator (just like the default/copy-constructor, a default one is generated for us when we do not write out our own). So take for example:
struct S {
int operator=(int) {
// do our own stuff
}
};
int main() {
S s;
s = 4;
}
This compiles, but it won't work for us in the general case where we want to assign another S object into s. This is because since we wrote our own, the compiler doesn't provide one for us. This is where default comes into play:
struct S {
int operator=(int) {
// do our own stuff
}
S & operator=(const S &) = default;
};
int main() {
S s1, s2;
s1 = s2; // legal
}
But technically it's superfluous to define the assignment operator, copy, or move constructor as default when there is no other function "overriding" it as the compiler will provide one for you in that case anyway. The only case in which it won't provide you one is where you define your own one (or variation thereof). But as a matter of preference, asking the compiler to provide a default function using the new aforementioned syntax is more explicit and easier to see and understand if intentionally left out for the compiler.
I have a class with a member which is not changed by the methods of the class, so I marked it as const. My problem is that I was using the default assignment operator just like a copy constructor in order to avoid multiple declarations. But in this case the assignment operator is not automatically generated, so I get some compiler errors:
'operator =' function is unavailable. This seems like that there is no real life scenario where const class members can be actually used (e.g. have you seen any const member in the STL code?).
Is there any way to fix this, beside removing the const?
EDIT: some code
class A
{
public :
const int size;
A(const char* str) : size(strlen(str)) {}
A() : size(0) {}
};
A create(const char* param)
{
return A(param);
}
void myMethod()
{
A a;
a = create("abcdef");
// do something
a = create("xyz");
// do something
}
Here's your misconception which is causing this issue:
[..] which is not changed by the methods of the class
The member variable is changed by a method of your class, the assignment operator. Including the one synthesized by the compiler. If you mark a member variable as const, this expresses that this variable will (should not!) change its value during the lifetime of the object. So clearly, assigning a new value to the object violates this statement. So if you indeed don't want the member to change, just don't make it const.
const members are ideal in many, many cases. of course, there is the obvious case where a value should not or must not change, but it's also an important restriction for optimization and concurrency -- not every type needs or should have an assignment operator.
if the member needs the behavior of assignment, then the variable must not be const.
when the value/member must not mutate or be mutated by this, it's clearer to provide a separate interface for the variable members (or even a subtype->composition in more complex cases):
class t_text {
public:
// ...
public:
void setString(const std::string& p);
private:
const t_text_attributes d_attributes;
std::string d_string;
};
therefore, my suggestion is to hide the assignment operator, and to make the 'mutable chunk' or member set-able for clarity:
text.setString("TEXT"); // << Good: What you read is what happens.
text = otherText; // << Bad: Surprise - attributes don't actually change!
You cannot have a const member and support assignment, at least not
assignment with the expected semantics. const is, logically, a
promiss that the member will never change, and assignment is
(implicitly, in the minds of most people) a promiss that all data
members will take the values of the members of the right hand side
(which normally means changing). There's a very definite conflict
between these two promisses.
Of course, a lot of types shouldn't support assignment to begin with;
for types that don't support assignment, there's no problem declaring a
data member const. On the whole, however, I've found const a lot
less useful here; const is part of a contract, and data members are
not usually part of the external contract of the class. (But a lot
depends—if the data member is public or protected, then the fact
that it is immutable could be part of the external contract. And of
course, there's nothing wrong with expressing internal class invariants
in language constructs, either.)
Yes, you can override the assignment operator.
Because you're using the default one, the compiler will try to copy the const members also. Which is illegal, since it's const.
class A
{
private:
const int a;
public :
A() : a(0) {}
A& operator = (const A& other) {return *this;}
};
int main()
{
A a;
A b;
a = b; //this is legal if operator = is declared
}
Consider this piece of code:
class complex{
private:
double re, im;
public:
complex(double _re, double _im):re(_re),im(_im){}
complex(complex c):re(c.re),im(c.im){}
};
I already knew that the copy constructor complex(complex c) will cause infinite recursion.
However, it should only pick const reference copy constructor complex(const complex &c) as the function for copying, since this is the default behavior if it is not explicitly specified. Every thing else is excluded, such as complex(complex c).
Why does it apply the function with pass by value here? Or both are copy constructors, except the pass by value cannot be modified and is used to pass to other functions rather than its constructor?
I think in Java, it is permitted to do like that, since it will just copy the object into the constructor.
A copy constructor must be in on of the following forms:
T(T&);
T(const T&);
That is, a constructor is a copy constructor only if it takes a single parameter of reference class type. A constructor that takes a single parameter of class type by-value is, by definition, not a copy constructor (edit) -- and in fact is illegal, as pointed out by Cat Plus Plus.
12.1 : Constructors
10/A copy constructor for a class X is a constructor with a first parameter of type X& or of type const X&
Beyond this Standardese, however, is a fundamental error in your understanding of the copy constructor. Consider the following code:
class Foo
{
public:
Foo() {}
Foo(Foo f) {/*...*/};
};
int main()
{
Foo f1;
Foo f2(f1);
}
When f2 is constructed f1 is passed by-value. In order to evaluate the parameters for the constructor call, f1 must be copied. So, you see there is a paradox here. In order to call the copy constructor, you must make a copy. In order to make a copy, you must call the copy constructor...
The above must call the constructor with an argument by-value simply because that is what the type of the parameter is: it's a Foo by-value. It it were not by-value, it would have to be either by reference, which looks like this:
Foo(Foo& rhs)
/* or like this: */ Foo(const Foo& rhs);
...or it must take it by pointer, which looks like this:
Foo(Foo* prhs)
...but, in the latter case, this is obviously not a copy constructor given the definition above.
complex(complex) is not a copy constructor. It's ill-formed, and should be rejected by compiler. There is no infinite recursion, because you simply cannot define a constructor like that.
A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).
A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified)
X and either there are no other parameters or else all other parameters have default arguments. A member
function template is never instantiated to produce such a constructor signature.
Answer to Java question since all other possible explanations are given:
//java code
public class Bar{
private int foo;
public Bar() { } // public no-args constructor
public Bar(Bar b) { foo = b.foo; } // copy constructor
}
In Java objects are references and not value objects like they are in C++. In C++ when you copy an object you create a copy of the object's state, internal variables etc. In Java it simply copies the reference. The object's state is not copied so there is actually no need to call the copy constructor like you do it in C++.