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;
}
Related
I have a bunch of questions related to this, I couldn't find exact answers.
Class A is the main class, and B is a subclass
If A::operator=(const A & other) was defined, does the default implementation of B::operator= copies members of B then calls A::operator= ?
If A copy constructor was defined, does the default implementation of the copy constructor for B copy-constructs members of B, then calls A copy constructor?
Do I need to define above functions virtual in A to get this behavior? (I suppose yes for operator= and no for the copy constructor, as virtual constructors is a nonsense?)
Can I forbid overloading the assignment operator or the copy constructor for subclasses of A, to force the use of default implementations?
The idea behind this, being to offer a plugin API for my users. I need the API to be C++ as scripts are too slow (I'll try JIT compilation one day), but it should be very simple.
If the default copy-assignment operator of class B is not deleted, [class.copy]/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 [i.e. in the order in which they're listed after class X : /*here*/ {/*...*/};], and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition.
Similarly, [class.copy]/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.
The order is: first the base class(es) (base class subobjects), then the direct non-static data members, similarly to 1).
For the behaviour described in 1) and 2), no. Virtual assignment operators are hardly ever useful. Constructors may not be virtual at all (doesn't make sense).
In order for a virtual function in a derived class B to override a virtual function in a base class A, it must have the same parameter types. That is, you could have a virtual A& operator=(A const&); in the base class A, but the override in class B had to look like virtual B& operator=(A const&);, which is not a copy-assignment operator for B, because of the parameter type.
Not without "hacks". And you're not actually overloading it, but hiding all base class assignment operators. Otherwise, this would be legal:
class A {};
class B { int i; };
A a;
B b = a; // using A::operator=(A const&)
Below is a simple example of how base methods are available in derived class:
struct Base {
void foo ();
Base& operator = (const Base&);
};
struct Derived : Base {
// ...
};
int main () {
Derived d1, d2;
d1.foo(); // calls Base::foo
d1 = d2; // calls Base::operator =
}
If I add below statements in the body of Derived to hide both the methods, ...
struct Derived : Base {
//...
private: // hide the below method for `Derived` objects
using Base::foo;
using Base::operator =;
}
... then the Base::foo() is hidden (becomes inaccessible) successfully.
But Base::operator = still remains accessible!!
d1.foo(); // error
d1 = d2; // ok !!
The same phenomena is happening for other operators as well. Here is a demo with g++.
Shouldn't the accessibility rules (applied due to using keyword) apply to the methods and operators in the same way?
If not then what's the significance of the statement: using operator =;, is it simply ignored by the compiler?
Update:
My first thought was that, compiler might be generating its own
default Derived::operator =. That's wrong because it uses
Base::operator =.
If I use private inheritance, then Base::foo() is automatically
hidden (even without using). But no effect on Base::operator =,
it still works.
Please note that, I don't want solution for "how to hide it". But would like to understand from language point of view, why the operators are not hidden like other methods.
Compiler will generate a default operator= for Derived class, and this implicit Derived::operator= in turn invokes Base::operator= internally.
To get rid of this, we need to disable the operator= explicitly:
struct Derived : Base {
private: Derived& operator = (const Derived&); // C++03 way
};
struct Derived : Base {
Derived& operator = (const Derived&) = delete; // C++11 way
};
Update:
My first thought was that, compiler might be generating its own default Derived::operator =. That's wrong because it uses Base::operator =. Same holds true for other operators as well.
If I use private inheritance, then Base::foo() is automatically hidden (even without using). But no effect on Base::operator =, it still works.
Your first though was correct. You didn't declare a copy assignment operator, so one was implicitly declared for you. You then odr-used that implicitly-declared copy assignment operator, so the compiler provided the implicitly-defined copy assignment operator. The implicitly-defined copy assignment operator of a struct or class performs a memberwise copy assignment of the class's base classes and then it's non-static data members.
That implicitly-defined copy assignment operator is a class member. It doesn't matter one bit that you have hidden the base class copy constructor to the outside world via using or via private inheritance because that base class assignment operator is visible to this implicitly-defined copy assignment operator.
I'm trying to understand this behaviour but it seems I don't. Please see this code:
#include <iostream>
using namespace std;
class Base
{
public:
void operator=(const Base& rf)
{
cout << "base operator=" << endl;
this->y = rf.y;
}
int y;
Base() : y(100) { }
};
class Derived : public Base
{
public:
int x;
Derived() : x(100) { }
};
int main()
{
Derived test;
Derived test2;
test2.x = 0;
test2.y = 0;
test.operator=(test2); // operator auto-generated for derived class but...
cout << test.x << endl << test.y << endl;
cin.ignore();
return 0;
}
PROGRAM OUTPUT:
> base operator=
> 0
> 0
Now where I'm confused is:
The rule says that a derived class never inherits the assigment operator, instead it creates its own operator= however in this example base's operator= gets invoked on the derived class.
Second I was able to explicitly invoke an assigment operator on a derived class, which isn't in turn explicitly defined in the derived class.
Now if I understand it correctly, this means that any user defined base's operator always gets invoked on the derived class?
The generated ones automatically call the base-class assignment operator.
// generated version looks basically like this
Derived& operator=(Derived const& other){
Base::operator=(static_cast<Base const&>(other));
x = other.x;
return *this;
}
The cast is there to avoid an accidential call to a templated Base::operator= like this one:
template<class Other>
Base& operator=(Other const& other); // accepts everything
Or a strange one like this:
// forward-declare 'Derived' outside of 'Base'
Base& operator=(Derived const& other); // accepts derived class (for whatever reason)
Second I was able to explicitly invoke an assigment operator on a derived class, which isn't in turn explicitly defined in the derived class.
The compiler automatically declares an assignment operator if you do not and your class allows it (i.e., no reference members and some other arcane rules) and additionally defines it if you actually use it somewhere.
The compiler-generated assignment operator calls the assignment operator of each subobject. That includes base classes and non-static member variables.
The standard says (section 12.8 [class.copy]):
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy constructor or a user-declared destructor. The implicitly-declared copy assignment operator for a class X will have the form
X& X::operator=(const X&)
if
each direct base class B of X has a copy assignment operator whose parameter is of type const B&,
const volatile B& or 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 assignment operator whose parameter is of type const M&, const volatile M& or M.
Otherwise, the implicitly-declared copy assignment operator will have the form
X& X::operator=(X&)
and
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.
It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined copy assignment operator.
That's because the implicitly defined operator = calls the base classes operator =. See the FAQ lite:
I'm creating a derived class; should my assignment operator call my base class's assignment operator?
Yes (if you need to define an assignment operator in the first place).
If you define your own assignment operator, the compiler will not automatically call your base class's assignment operator for you. Unless your base class's assignment operator itself is broken, you should call it explicitly from your derived class's assignment operator (again, assuming you create one in the first place).
However if you do not create your own assignment operator, the one that the compiler creates for you will automatically call your base class's assignment operator.
4 things never get inherited
Constructor
Copy-constructor
Assignment operator
Destructor
Even you have not written the assignment operator your code will be Woking fine.
Whenever you use assignment operator compiler will make sure that assignment operator for each member object and base class get called.
I'm confused... why isn't my assignment operator getting called here?
template<typename This>
struct mybase
{
This& operator =(const This &other)
{
__debugbreak(); // The debugger should break here, but doesn't.
return static_cast<This &>(*this):
}
};
struct myderived : mybase<myderived>
{
int x;
};
int main()
{
myderived a = myderived(); // And yes, I know it's redundant...
myderived b = myderived();
a = b;
return 0;
}
Because the default assignment operator in derived will hide the overloaded in base.
mybase::operator= is hidden by the automatically generated copy assignment operator myderived::operator=.
You can use a using declaration to make the base class operator visible in the derived class.
EDIT: added example per request:
template<typename This>
struct mybase
{
This& operator =(const This &other)
{
//__debugbreak(); // The debugger should break here, but doesn't.
return static_cast<This &>(*this);
}
};
struct myderived : mybase<myderived>
{
using mybase<myderived>::operator=;
int x;
};
int main()
{
myderived a = myderived(); // And yes, I know it's redundant...
myderived b = myderived();
a = b;
}
This compiles fine with Visual C++ 10.0 and with Comeau Online. The latter means, in practice, that it's good standard C++. However, the code does not compile with MinGW g++ 4.4.1 (compiler bug).
EDIT 2: Actually, checking now, with Visual C++ 10.0 it compiles but the base class operator is not invoked. So maybe g++ is correct. using is generally the way to bring in a base class assignment operator (or whatever), but in this case it has the same signature as the derived class’ copy assignment operator, and I do not yet know whether Visual C++ behavior is correct or not – it is a corner case of the language.
EDIT 3: I checked N3290 (the standard draft that is identical to C++11), and it says
§12.8/18:
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4).
I personally interpret that as saying that with the using declaration in place the class “declares” a copy assignment operator, and that one should therefore not be implicitly generated (as it seems that Visual C++ 10.0 does). However, this is a corner case of the language. Others may possibly interpret this differently, and as noted above, compilers differ!
Cheers & hth.,
This line:
a = b;
obviously requires that myderived have overloaded the copy assignment operator. It can be implicitly generated by the compiler, or defined by the myderived class explicitly:
12.8 Copying class objects [class.copy]
9. A user-declared copy assignment operator X::operator= is a
non-static non-template member function of class X with exactly one
parameter of type X, X&, const X&, volatile X& or const
volatile X&.
You have attempted to create a user-declared copy assignment operator in your mybase class, but it's not actually a copy assignment operator according to the C++ standard. Imagine if we did a type substitution for This with myderived:
// Hypothetical class generated by the compiler from
// the mybase template class with This = myderived
struct mybase_myderived
{
myderived& operator =(const myderived &other)
{
// ...
}
};
Clearly that's not a copy assignment operator, because the parameter other is of type const myderived&, not const mybase&. Had the other parameter be of type const mybase&, or mybase, or mybase&, then it would be a valid copy assignment operator, which can be called by the default copy assignment operator in myderived. But it isn't in this case, so the compiler still generates a default copy assignment operator for mybase, which of course does nothing in this case.
The compiler-generated default copy assignment operator in myderived calls the compiler-generated default copy assignment operator in mybase. So what ends up happening, as As a result, the operator=(const myderived &other) overload is never called.
The reason why the compiler doesn't just call mybase::operator= directly is because it's been hidden by the compiler-generated copy assignment operator in myderived, as Alf P. Steinbach points out in this answer.
Because the compiler introduces the default assignment operator in myderived. Override it and call your base assignment operator yourself. Or perhaps a using directive will help? Try using mybase::operator= in myderived body.
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;
};