Virtual assignment operator not allowing static_cast - c++

I have the following MWE code:
#include <algorithm>
class Base{
public:
int baseMember;
friend void swap(Base& in, Base& out)
{
using std::swap;
swap(in.baseMember, out.baseMember);
}
virtual Base& operator=(Base obj)
{
swap(*this, obj);
return *this;
}
Base() : baseMember(1)
{
}
};
class Derived : public Base
{
public:
int derivedMember;
friend void swap(Derived& in, Derived& out)
{
using std::swap;
swap(in.derivedMember, out.derivedMember);
swap(static_cast<Base&>(in), static_cast<Base&>(out));
}
virtual Base& operator=(Base obj)
{
swap(*this, static_cast<Derived&>(obj));
return *this;
}
Derived() : Base(), derivedMember(2)
{
}
};
int main()
{
Base *b1 = new Derived();
Base *b2 = new Derived();
*b1 = *b2;
delete b1;
delete b2;
}
I have two Base pointers pointing to Derived data. I then do an assignment of the contents of the pointers. Since the Base class' assignment operator is virtual, polymorphism kicks in and the assignment operator of the Derived class (with the same signature) is called instead.
However, the static_cast to transform the source into a Derived object fails. Or, well, it successfully transforms it into a Derived object, but its derivedMember is garbage (after being initially set in the constructor).
How can this be avoided? How can an assignment between the Derived contents of two Base pointers be done?

Your code has a typo and inherently unsafe behavior. The typo is here:
virtual Base& operator=(Base obj) // <-- should be base&
{
swap(*this, static_cast<Derived&>(obj));
return *this;
}
If you fix this, your code should work in the simple example provided. But it would still fail at large, because how would you guarantee the argument passed to operator= will be in fact of Derived type?

Passing an argument by value slices that argument. So by the time your operator= function is called, you have an actual Base, not a Derived.
I'm afraid you'll need to overload your assignment for lvalues and rvalues.
virtual Base& operator=(const Base&);
virtual Base& operator=(Base&&);

Related

Implementing copy constructor for polymorphic class

Suppose I'm dealing with the following header file(for experimental purposes):
class Base{
protected:
Data& data1;
public:
Base(Data& data1);
virtual Base* clone() const =0;
virtual ~Base();
virtual void do() = 0;
};
class Derived: public Base{
private:
const int data2;
public:
Derived(int data2, Data& data1);
virtual Derived* clone() const;
~Derived();
}
I tried to implement these classes which are fairly simple:
Base:
Base::Base(Data& data1): data1{data1} {}
virtual Base::~Base() = default;
Derived:
Derived::Derived(int data2, Data& data1): Base(data1), data2{data2} {}
virtual Derived* Derived::clone() const {
return new Derived(*this);
}
virtual void Derived::do() {
return;
}
So far so good, but I'm having a couple of issues:
Say I wanted to implement a copy constructor and a copy assignment operator for this hierarchy,
should I do it in both classes? If so, is this a good way to do it? :
Base:
Base::Base(const Base& other): Base(other.data1) {}
Base& Base::operator=(const Base& rhs){
if(this == &rhs)
return *this;
data1 = rhs.data1; // is this possible? since data1 is a reference (cannot be reassigned)
return *this;
}
and which is a proper way to do so for derived?:
Derived:
Derived::Derived(const Derived& other): Base(other.data1), data2{other.data2} {}
OR
Derived::Derived(const Derived& other): Derived(other.data2, other.data2) {}
Derived& Derived::operator=(const Derived& rhs){
if(this == &rhs)
return *this;
data1 = rhs.data1;
data2 = rhs.data2; // data2 is a const variable so this is probably invalid too
return *this;
}
Once I implement a copy constructor for derived, I get an 'Endless loop' warning when returning from clone() method. Why is that?
Is there a proper way to implement rule of 3/ rule of 5 for classes of such hierarchy?
Thanks ahead.
Say I wanted to implement a copy constructor and a copy assignment operator for this hierarchy, should I do it in both classes? If so, is this a good way to do it?
Yes, you have to provide copy constructor/assignment.
Best way, when possible/correct, is to use default.
For assignment, your issue is unrelated to hierarchy, but related to member you use:
Reference cannot be rebound. const member cannot be changed.
So default assignment cannot be generated, mark them as deleted.
class Base
{
// ...
Base(const Base&) = default;
Base& operator=(const Base&) = delete;
};
class Derived
{
// ...
Derived(const Derived&) = default;
Derived& operator=(const Derived&) = delete;
};
Once I implement a copy constructor for derived, I get an 'Endless loop' warning when returning from clone() method. Why is that?
Cannot reproduce from comment
Is there a proper way to implement rule of 3/ rule of 5 for classes of such hierarchy?
Hierarchy is unrelated to rule of 5/3/0.
Just respect rule of 5/3/0 for each class.

How to implicitly convert returned value to base class without invoking copy constructor?

Consider two classes such as these:
#include <cstdio>
using std::puts;
class Base {
public:
Base() { puts("Create base"); }
Base(const Base &) { puts("Copy base"); }
Base(Base &&) { puts("Move base"); }
virtual ~Base() { puts("Delete base"); }
Base & operator=(const Base &) = delete;
};
class Derived : public Base {
public:
Derived() { puts("Create derived"); }
Derived(const Derived &) { puts("Copy derived"); }
Derived(const Base &) { puts("Copy derived from base"); }
Derived(Derived &&) { puts("Move derived"); }
Derived(Base &&) { puts("Move derived from base"); }
virtual ~Derived() { puts("Delete derived"); }
Derived & operator=(const Derived &) = delete;
};
and a function:
Base fn() {
Derived d;
// Fill in d here
return d;
}
Copying the base class is a very expensive operation, however because the derived class is not much different, it could be converted into a base object using move semantics. However, I can't get the compiler to use that implicitly instead of the copy construction. I tried adding the following with no success:
Base::Base(Derived &&);
Derived::operator Base &&() &&;
Derived::operator Base() &&;
Is there a way to avoid the copy constructor by only changing the two classes and not the function fn?
EDIT: I know how to do it if I could change the function fn but I can't.
Base::Base(Derived &&); would work fine if added correctly.
#include <utility>
class Derived;
class Base {
// ...
Base(Derived &&);
};
// Derived here
Base::Base(Derived &&d) : Base(std::forward<Base>(d)) {}
The only tricky part is std::forward<Base>(d). But it's just a cast ultimately, where we are asking to forward d as a Base xvalue. Then the delegated move constructor does the right thing.
See it live
RVO is guaranteed to try and return the local variable as though it was an rvalue first. Since now there is a constructor matching it, it will work.

C++ copy constructors and assignments in cloneable hierarchy

There is well-known clone idiom for copying Derived objects via pointer to Base class.
class Base{
int b;
public:
virtual unique_ptr<Base> clone() const = 0;
virtual ~Base() = default;
};
class Derived : public Base {
int d;
public:
virtual unique_ptr<Base> clone() const override {
return std::make_unique<Derived>(*this);
}
}
However, I can't find clear instructions how to define copy constructors and assignments in this case. This is how I suppose it should be done in Base class:
class Base {
protected:
Base(const Base&) = default;
private:
Base& operator=(const Base&) = delete;
}
Is it necessary (in order to avoid potential slices)? Is it right way to do it? Does it suffice or should I add such declarations to Derived class as well?
As derived classes use the copy constructor to create clones, you may like to make the copy constructors non-public to avoid accidental slicing but accessible to derived classes.
protected fills this requirement. It needs to be applied to each class' copy constructor because the compiler-generated copy constructor is public. It also makes sense to apply the same treatment to the assignment operator.
That also prevents std::make_unique from accessing the copy constructor though:
class A
{
protected:
A(A const&) = default;
A& operator=(A const&) = default;
public:
A();
virtual std::unique_ptr<A> clone() const = 0;
};
class B : public A
{
protected:
B(B const&) = default;
B& operator=(B const&) = default;
public:
B();
std::unique_ptr<A> clone() const override {
return std::unique_ptr<A>(new B(*this));
}
};
Deleting the copy assignment operator is probably a good idea, unless you need it.
Deleting operator=(const Base&) in Base is enough, as the implicitly declared copy assignment operator is defined as deleted for a derived class if the base class has no copy assignment operator (see cppreference.com).
If you really want copy assignment you can make the copy assignment operator virtual, and carefully implement the correct behaviour in the derived classes by
calling Base::operator= to assign the Base class members, and
assigning the members of the derived class, using dynamic_cast to ensure that the argument is of the correct type .
If done correctly, this avoids object slicing and retains the correct type.
An example (with copy constructor details omitted):
struct Point {
virtual Point& operator=(const Point& p) =default;
int x;
int y;
};
struct Point3d :public Point{
virtual Point3d& operator=(const Point& p);
int z;
};
Point3d& Point3d::operator=(const Point& p)
{
Point::operator=(p);
auto p3d = dynamic_cast<const Point3d*>(&p);
if(p3d){
z = p3d->z;
} else {
z = 0;
}
return *this;
}

Assigning base class members in copy assignment operator

I've got a class that inherits from a MSFT class, and therefore cannot be changed, and I'd like my derived class to have identical behavior for its copy constructor and copy assignment operator. The trouble I'm having is that in the copy constructor, you are free to invoke a constructor for the base class in the initializer list, but in the operator, this is not an option. How can I properly recreate this behavior in the assignment operator? Is it sufficient to just call the base class's constructor in the body of the operator overload?
Additional note: the base class inherits from CObject, which has operator=() and the copy constructor as private and unimplemented methods, so unfortunately any calls to those will result in a compile error.
I've provided a simplified code scenario below:
Class declarations:
class Base
{
protected:
int baseInt;
public:
Base(int);
}
class Derived : public Base
{
public:
Derived(const Derived& other);
Derived& operator=(const Derived& rhs);
private:
int derivedInt;
}
Derived class member functions:
// Copy Constructor
Derived(const Derived& other) : Base(5)
{
derivedInt = other.derivedInt;
}
// Copy Assignment Operator
Derived& operator=(const Derived& rhs)
{
if (&rhs != this)
{
derivedInt = other.derivedInt;
return *this;
}
}
EDIT: Updated syntax and added CObject note
In the general case, you do that either by explicitly calling operator= for the base class subobject, or by using the copy&swap idiom, if available:
//explicit call to base operator=
Derived& operator=(const Derived& rhs)
{
Base::operator=(rhs); //
derivedInt = rhs.derivedInt;
return *this;
}
//or copy&swap:
Derived& operator=(const Derived& rhs)
{
Derived tmp(rhs); //copy
tmp.swap(*this);
return *this;
}
void swap(Derived& other)
{
Base::swap(other);
std::swap(derivedInt,other.derivedInt);
}
Update: since your base class is not meant to be copy-assigned, your derived class should not be copy-assigned either. There are some cases where classes contain noncopyable parts but are perfectly copyable. In those cases the noncopyable part is something that does not directly contribute to the class objects' state, e.g. a unique ID. Then those parts will normally not be changed during assignment. However, in those cases the noncopyable parts should not be contained by inheritance but rather by aggregation.
Shortly said: Iheritance means "is-A" relationship. Your Base cannot be copied. Your Derived is a Base. Thus your Derived cannot be copied either.
Like this
Derived& operator=(const Derived& rhs)
{
Base::operator=(rhs);
derivedInt = rhs.derivedInt;
return *this;
}

calling operators of base class... safe?

Is following pattern ok/safe ? Or are there any shortcomings ?
(I also use it for equality operators)
Derived& operator=(const Derived& rhs)
{
static_cast<Base&>(*this) = rhs;
// ... copy member variables of Derived
return *this;
}
This is fine, but it's a lot more readable IMHO to call the base-class by name:
Base::operator = (rhs);
Yes, it's safe.
A different syntax to do the same thing could be:
Base::operator=( rhs );
That's better to use
Base::operator=(rhs);
because if your base class have a pure virtual method the static_cast is not allowed.
class Base {
// Attribute
public:
virtual void f() = 0;
protected:
Base& operator(const Base&);
}
class Derived {
public:
virtual void f() {};
Derived& operator=(const Derived& src) {
Base::operator=(src); // work
static_cast<Base&>(*this) = src; // didn't work
}
}