Assignment of base class object to derived class object - c++

Why do I get error: no match for ‘operator=’ in ‘y = x’ in the following code?
Cant the a-component of b not just be assigned to like it was a-object = a-object?
struct a {int i;};
struct b : public a {int j;};
int main()
{
a x;
b y;
x.i = 9;
y.j = 5;
y = x; // error: no match for ‘operator=’ in ‘y = x’
// expected: y.i = 9
return 0;
}

You are not explicitly defining any assignment operators, so the compiler will generate its own default operators for each struct. The compiler's default assignment operator in b takes a b as input and will assign both members. And assignment operators are not automatically inherited when using inheritance. That is why you cannot pass an a to a b - there is no assignment operator in b that takes an a as input. If you want to allow that, you need to tell the compiler as much, eg:
struct a
{
int i;
a& operator=(const a &rhs)
{
i = rhs.i;
return *this;
}
};
struct b : public a
{
int j;
using a::operator=;
b& operator=(const b &rhs)
{
*this = static_cast<const a&>(rhs);
j = rhs.j;
return *this;
}
};
int main()
{
a x;
b y;
b z;
...
y = x; // OK now
y = z; // OK as well
...
return 0;
}

No, because even if the classes are related they are different types.
Think about this, even if it was allowed and it would work like you expected, what would the value of y.j be after the assignment?

As the error states, you need to implement an assignment operator. That is, a function that tells your program how to assign one object to another one. You can find plenty of information on it if you search the web, e.g. on http://www.cplusplus.com/articles/y8hv0pDG/

Related

What is the meaning of assigning to return value of getter taking this pointer by value?

This doubt came to me when I jumped on an existing code and mistakenly used a getter to set a property,
obj.getProp() = otherProp;
instead of calling the setter,
obj.setProp(otherProp);
I did not realize the mistake because there was no error at compilation or runtime; the assignment resulted in a no-op.
So I came up with the following example, which outputs 337:
#include <iostream>
struct A {
int x = 0;
A(int x) : x(x) {}
A(A& a) : x(a.x) {}
void operator=(A const& other) { x = other.x; }
};
struct B {
A a{3};
int x{3};
A getAbyVal() { return a; }
A& getAbyRef() { return a; }
int getXbyVal() { return x; }
};
int main() {
B b;
std::cout << b.a.x; // this and the other two cout print what I expect, but...
b.getAbyVal() = A{7}; // ... I expected this to fail at compilation time in the first place...
//b.getXbyVal() = 3; // ... just like this fails.
std::cout << b.a.x;
b.getAbyRef() = A{7};
std::cout << b.a.x;
}
So my question is two folds:
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so
that the former compiles and the latter doesn't (beside the fact that
the types are A and int)?
Surprisingly, the difference in types is exactly what makes one compile correctly, and other to fail.
A has an assignment operator defined for it, so compiler dutifully invokes it on the return value (only to discard the whole object later). But the code you wrote supports this. From the compiler view, some other interesting things might have happened in your assignment operator, despite the fact that the object will be eradicated (side effects in formal parlance).
With int as a return value, compiler knows there are no side effects of assigning value to int, so assigning any value to object which is to be eradicated immediately does not make any sense.
what in b.getAbyVal() = A{7}; is different from b.getXbyVal() = 3; so that the former compiles and the latter doesn't (beside the fact that the types are A and int)?
The difference is precisely that one functions returns a class type, and the other function returns a POD type. A temporary int, for example can't be assigned to:
42 = x; // error
so similarly the language disallows assigning to a temporary int returned from a function as well. This is not the default behavior for user-defined class types so assigning to a temporary A compiles:
A{} = x; // ok
changing void operator=(A const& other) { x = other.x; } to void operator=(A const& other) & { x = other.x; } makes b.getAbyVal() = A{7}; fail to compile. Why is this the case?
Adding a & at the end of is called a ref-qualifier, and allows a user-defined class to have the same semantics as a POD type when it comes to assigning to a temporary. Adding a & at the end of operator= constrains it to only be used on an l-value (basically, a named variable, or a reference returned from a function).
A{} = x; // now error
A a;
a = x; // still ok

assigning class a value on declaration does not compile

I want to assign a class a value on declaration so I made this basic class:
class A
{
public:
A &operator=(int)
{
return (*this);
}
};
and compiled it with this main:
int main(void)
{
A x = 1;
}
but the compiler complained with this error message:
no viable conversion from 'int' to 'A'
A x = 1;
^ ~
but when I compile with this main:
int main(void)
{
A x;
x = 1;
}
everything compiles smoothly
why does my first main not compile and how can I change the class A so that it compiles?
A x = 1; is initialization, not assignment; they're different things. It doesn't invoke assignment operator but requires converting constructor.
class A
{
public:
// converting constructor
A (int) {}
A &operator=(int)
{
return (*this);
}
};
then
A x = 1; // initialize x via converting constructor
x = 2; // assign x via assignment operator

Marking a member variable to not copy

I have a class that contains POD members. I need to have all members copied, except one (member a in the example case). The way I'm doing it right now is as follows:
class Example
{
private:
int a = 0;
int b = 0;
int c = 0;
int d = 0;
double e = 0;
public:
Example& operator=(const Example &rhs)
{
b = rhs.b;
c = rhs.c;
d = rhs.d;
e = rhs.e;
return *this;
}
Example() {};
Example(const Example& toCopy)
{
*this = toCopy;
}
};
Is there a way to mark the variable to not copy, as doing it this way is verbose and prone to bugs if I later modify this class?
You can wrap the "odd man out", a, in a struct and define that structs behavior separately:
class Example
{
private:
CopyLess a;
int b = 0;
int c = 0;
int d = 0;
double e = 0;
struct CopyLess {
int a = 0;
CopyLess& operator=(const CopyLess&) { return *this; }
CopyLess(const CopyLess&) {}
};
};
Note that I didn't bother writing any special members for Example anymore because the defaults do what you want. Writing code this way to avoid writing special members as much as possible is called the "Rule of Zero", more information and examples: http://www.nirfriedman.com/2015/06/27/cpp-rule-of-zero/.
No, you cannot make the implicit copy assignment skip members. Defining a custom assignment operator as you have done is the way to achieve your desired behaviour.
Doing it this way is verbose and prone to bugs if I later modify this class
You can group the copied members into a single subobject. That way the custom copy assignment is not fragile to changes and verbosity remains constant in relation to number of copied members.
Bonus: You don't need to specify value initialization for the members separately, but one for the entire subobject is enough.
class Example
{
int a = 0;
struct {
int b;
int c;
int d;
double e;
} state = {};
public:
Example& operator=(const Example &rhs)
{
state = rhs.state;
return *this;
}
...
Since the members are private this won't change the existing API of the class.

Overloading Multiple Functions with Same Parameters

I'm given to understand, thanks to the VC++ compiler, that you cannot overload functions with only a differing return type.
class MyClass {
public:
MyClass MyClass::operator+(MyClass other) {
return MyClass(n + other.getn());
}
MyClass() = default;
MyClass(int my_n) : n{ my_n } {}
int getn() {
return n;
}
private:
int n{ 0 };
};
int main(){
MyClass m1(7), m2(5);
MyClass m3 = m1 + m2;
return 0;
}
However, what if I would like to return an integer, say 12, when I add them, I cannot simply add them together and overload the operator+ again because it doesn't allow overloading where the only difference is the return type. I'm very new to C++. The only solution I could come up with is:
int i = (m1+m2).getn();
But that seems wasteful to create an instance when you will never use it again.
I think what you want is to be able to write int i = m1 + m2;, which means you should create a conversion operator.
class MyClass
{
operator int()
{
return n;
}
};
int i = m1 + m2; //creates temporary, calls operator int, saves value in i
It will still create a temporary, but that's how operator+ works. You could always write int i = m1.getn() + m2.getn();, too.
Overloading doesn't consider about return type.
From the standard, $13.3/1 Overload resolution [over.match]
The selection criteria for the best function are the number of arguments, how well the arguments
match the parameter-type-list of the candidate function, how well (for non-static member functions) the object matches the implicit object parameter, and certain other properties of the candidate function.
and
128) ... candidate call functions that cannot be differentiated one from the other by overload
resolution because they have identical declarations or differ only in their return type.
You might want to provide two operator+ for MyClass, they only differ at the return type of MyClass and int, and use them as
MyClass m1(7), m2(5);
MyClass m3 = m1 + m2;
int x = m1 + m2;
but the return value could be omitted, then which one should be invoked? It's ill-formed.
m1 + m2;
// or m1.operator+(m2);
You could provide another named member function for it, such as
class MyClass { public:
int add_return_int(MyClass other) {
return n + other.getn();
}
...
or as free function
int add_return_int(MyClass lhs, MyClass rhs) {
return lhs.getn() + rhs.getn();
}

Temporaries created during assignment operation

if every assignment creates a temporary to copy the object into lvalue, how can you check to see in VC++ 8.0?
class E
{
int i, j;
public:
E():i(0), j(0){}
E(int I, int J):i(I), j(J){}
};
int main()
{
E a;
E b(1, 2);
a = b //would there be created two copies of b?
}
Edit:
Case 2:
class A
{
int i, j;
public:
A():i(0), j(0){}
};
class E
{
int i, j;
A a;
public:
E():i(0), j(0){}
E(int I, int J):i(I), j(J){}
E(const E &temp)
{
a = temp; //temporary copy made?
}
};
int main()
{
E a;
E b(1, 2);
a = b //would there be created two copies of b?
}
From your comment, you made it clear that you didn't quite understand this C++-FAQ item.
First of all, there are no temporaries in the code you presented. The compiler declared A::operator= is called, and you simply end up with 1 in a.i and 2 in a.j.
Now, regarding the link you provided, it has to do with constructors only. In the following code :
class A
{
public:
A()
{
s = "foo";
}
private:
std::string s;
};
The data member s is constructed using std::string parameterless constructor, then is assigned the value "foo" in A constructor body. It's preferable (and as a matter of fact necessary in some cases) to initialize data members in an initialization list, just like you did with i and j :
A() : s("foo")
{
}
Here, the s data member is initialized in one step : by calling the appropriate constructor.
There are a few standard methods that are created automatically for you if you don't provide them. If you write
struct Foo
{
int i, j;
Foo(int i, int j) : i(i), j(j) {}
};
the compiler completes that to
struct Foo
{
int i, j;
Foo(int i, int j) : i(i), j(j)
{
}
Foo(const Foo& other) : i(other.i), j(other.j)
{
}
Foo& operator=(const Foo& other)
{
i = other.i; j = other.j;
return *this;
}
};
In other words you will normally get a copy constructor and an assignment operator that work on an member-by-member basis. In the specific the assignment operator doesn't build any temporary object.
It's very important to understand how those implicitly defined method works because most of the time they're exact the right thing you need, but sometimes they're completely wrong (for example if your members are pointers often a member-by-member copy or assignment is not the correct way to handle the operation).
This would create a temporary:
E makeE( int i, int j )
{
return E(i, j);
}
int main()
{
E a;
a = makeE( 1, 2 );
E b = makeE( 3, 4 );
}
makeE returns a temporary. a is assigned to the temporary and the assignment operator is always called here. b is not "assigned to", as it is being initialised here, and requires an accessible copy-constructor for that to work although it is not guaranteed that the copy-constructor will actually be called as the compiler might optimise it away.