Why isn't my assignment operator getting called? - c++

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.

Related

Relationship between assignment operator and user-defined constructor

#include <iostream>
class A{
};
class B: public A{
public:
B(A&& inA){
std::cout<<"constructor"<<std::endl;
}
};
int main(){
B whatever{A{}};
whatever=A{};
}
This outputs
constructor
constructor
at least with C++14 standard and GCC. How is it defined that assignment operator can result in call to constructor instead of operator=? Is there a name for this property of assignment operator?
Since you meet all the conditions for generating a move-assignment operator. The move-assignment operator the compiler synthesizes for you is in the form of:
B& operator=(B&&) = default;
Recall that temporaries can be bound to const lvalue references and rvalue references. By Implicit Conversion Sequences, your temporary A{} is converted to a temporary B which is used to make the move assignment. You may disable this with explicit constructors.

Hiding base class method with "using" declaration doesn't work for assignment operator

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.

C++ copy-constructor and assignment

I've had it drilled into my head many many times that if a copy-constructor is provided, an assignment operator must also be provided. However, there are times when a class can use a copy-constructor but not an assignment operator.
For example:
class A {
public:
const int myVar;
A(const int var) : myVar(var) {};
A(const A& other) : myVar(other.myVar) {};
};
So is this a terrible thing to do? Does the assignment operator need to be defined but made private? Is such a class still copy-constructable?
So is this a terrible thing to do?
No, it is not.
Not all classes need to be copy constructible as well as assignable. It is perfectly valid to have copy constructible yet non assignable classes.
Is such a class still copy-constructable?
Yes it is.
As long as your class provides a public copy constructor, Your class is copy constructible.
Does the assignment operator need to be defined but made private?
It depends on your usage.
If your class needs to be assignable then it should ideally not have a const member.
The default compiler generated copy assignment operator will not work if your class has an const member because it tries to assign to a const member which is not allowed. So if your code needs a copy assignment operator you will have to provide your own overloaded version. But, Anyway this overloaded version cannot provide expected assignment semantics.
If your class objects do not need to be Assignable then do not define it. If your code does accidentally uses it the compiler will generate an error anyways.

Do derived classes indirectly inherit base's 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.

Implementation supplied copy constructor and assignment operator

I have a small confusion regarding the situations where the implementation (compiler) will not supply the copy constructor and the copy assignment operator.
When we declare the copy ctor and/or copy assignment operator in our class.
Some says when we derive from a class which has a private copy ctor and/or copy assignment operator.
I am a little confused about the second situation, is the second situation is precisely.
a) The implementation will not declare them for you, so you will get a compile time error.
OR
b) The implementation will declare and define them, but when the compiler defined implementation tries to find the base class' method, we will get a compile time error.
I had an interview yesterday, I said its (b) that is happening but the interviewer disagrees, he says its (a).
I tried to compile the following code in both Microsoft C/C++ 14.00 and gcc 4.4.5
struct A
{
private:
A& operator = ( const A& );
};
struct B : A
{
};
int main()
{
B b1;
B b2;
b1 = b2;
return 0;
}
Microsoft compiler output
ctor01.cpp(9) : error C2248: 'A::operator =' : cannot access private member declared in class 'A'
ctor01.cpp(4) : see declaration of 'A::operator ='
ctor01.cpp(2) : see declaration of 'A'
This diagnostic occurred in the compiler generated function 'B &B::operator =(const B &)'
gcc compiler output
Ctor01.cpp: In member function ‘B& B::operator=(const B&)’:
Ctor01.cpp:4: error: ‘A& A::operator=(const A&)’ is private
Ctor01.cpp:8: error: within this context
Ctor01.cpp: In function ‘int main()’:
Ctor01.cpp:15: note: synthesized method ‘B& B::operator=(const B&)’ first required here
So I think, the implementation will declare and define it, but when the compiler defined implementation tries to find the base class method, we will get a compile time error. Correct me if I am wrong.
Regarding the copy constructor, this is what the standard says (12.8/7) :
A program is illformed if the class
for which a copy constructor is
implicitly defined has:
a nonstatic data member of class type (or array thereof) with an
inaccessible or ambiguous copy
constructor, or
a base class with an inaccessible or ambiguous copy
constructor.
Regarding the copy assignment operator (12.8/12) :
A program is illformed if the class
for which a copy assignment operator
is implicitly defined has:
a nonstatic data member of const type, or
a nonstatic data member of reference type, or
a nonstatic data member of class type (or array thereof) with an
inaccessible copy assignment operator,
or
a base class with an inaccessible copy assignment operator.
How the compiler reports the error, or how it actually falls into it, is pretty much irrelevant from my point of view.
However, I do believe that answer (b) is probably more correct : the base class copy assignment is declared, and it's inaccessible. The derived class has an implicitly declared copy assignment which the compiler will try to define if used, thus making the program ill-formed.
A class will have a copy constructor and a copy assignment operator implicitly declared if there is no user declared version of either. This always happens.
Simplistically, the implementation will implicitly define these only if they are actually used. If, when the implementation tries to define them, the implicit definition would be ill-formed (e.g. for copy-assignment the class contains a reference member or const member or for the copy constructor a base or member has private copy constructor) then the program is ill-formed.
A program can still be valid if it contains classes which have implicitly declared copy constructors and copy assignment operators which cannot be implicitly defined so long as it does not cause these to actually be defined by using them or causing them to be used.
Your case (b) is more accurate.
C++03 Standard 12.8p10
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly.
And 12.8p12
An implicitly-declared copy assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type. A program is ill-formed if the class for which a copy assignment operator is implicitly defined has:
a nonstatic data member of const type, or
a nonstatic data member of reference type, or
a nonstatic data member of class type (or array thereof) with inaccessible copy assignment operator, or
a base class with an inaccessible copy assignment operator.
The corresponding requirements for implicitly defined copy constructors, default constructors, and destructors have similar wordings.
Specifying that the methods exist even though their definitions will be illegal clarifies some things about overload resolution. For example,
class A {
private:
A& operator=(const A&);
};
class B : public A {
public:
operator int() const;
B& operator=(int);
};
void f(B& b1, const B& b2)
{ b1 = b2; }
is illegal because the implicitly-declared B::operator=(const B&) is the better overload but the implicit definition is ill-formed. Without that declaration, you might think the compiler should implicitly convert b2 to int and then assign that to b1.
I think the distinction between the two depends on the details of your specific implementation (and doesn't make much difference). For what it's worth, Comeau gives this:
"ComeauTest.c", line 7: error: "A &A::operator=(const A &)" (declared at line 4) is
inaccessible
struct B : A
^
detected during implicit generation of "B &B::operator=(const B &)"
at line 16
1 error detected in the compilation of "ComeauTest.c".
So on that compiler, it detects the error "during" the implicit generation of B's assignment operator. In other words, it tries to generate it, and finds that it can't. Whether it detects it as it's writing it out, or by looking at A directly, doesn't really matter.
This is what happens :
struct A
{
private:
A& operator = ( const A& );
};
struct B : A
{
B& operator = ( const B& other )
{
A::operator=( other );
return *this;
}
};
int main()
{
B b1;
B b2;
b1 = b2;
return 0;
}
The default operator= tries to call A::operator=, which is private.
The standard seems to agree with you. Quoting from the current draft:
§12.8/8:
If the class definition does not
explicitly declare a copy constructor
and there is no user-declared move
constructor, a copy constructor is
implicitly declared as defaulted
(8.4).
§12.8/12:
A defaulted copy/move constructor for
a class X is defined as deleted
(8.4.3) if X has: […]
a direct or virtual base class B that cannot be copied/moved because
overload resolution (13.3), as applied
to B’s corresponding constructor,
results in an ambiguity or a function
that is deleted or inaccessible from
the defaulted constructor […]
So the synthesized copy constructor is declared and defined, but defined as deleted.