How to clone an object without copy constructor - c++

According to CppCoreGuideline, I should disable the copy constructor of a base class and propose a clone method: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rc-copy-virtual
For example:
class B {
public:
explicit B() = default;
B(B&&) = default; // Move constructor
B& operator=(B&&) = default; // Move assignment operator
B(const B&) = delete; // Copy constructor
B& operator=(const B&) = delete; // Copy assignment
virtual ~B() = default;
virtual unique_ptr<B> clone()
{
return unique_ptr<B>{new B{*this}}; // how do this without copy constructor ?
}
private:
int c;
int d;
};
class D : public B {
public:
explicit D() = default;
D(D&&) = default; // Move constructor
D& operator=(D&&) = default; // Move assignment operator
D(const B&) = delete; // Copy constructor
D& operator=(const D&) = delete; // Copy assignment
virtual ~D() = default;
virtual unique_ptr<B> clone() override
{
// how can I copy all private data member of base class ???
}
};
but how can I copy all private data member in clone method? Obviously I'll use the CRTP pattern : C++: Deep copying a Base class pointer

I think the simplest way is to actually make the special members protected instead of deleted. This still prevents slicing, but makes it easier to implement clone(). Note that both the copy and move members need to be treated this way.
class B {
public:
// if this is truly intended to be a polymorphic base class, it probably
// doesn't make sense for the base to be able to clone itself.
virtual unique_ptr<B> clone() = 0;
protected:
B(B const& ) = default;
B& operator=(B const& ) = default;
private:
int c;
int d;
};
Which also allows the derived classes to do this easily:
class D : public B {
public:
D(D const& ) = default; // this is safe now
D& operator=(D const& ) = default;
unique_ptr<B> clone() override {
return unique_ptr<D>(new D(*this));
}
// ...
};

Rather than disabling the copy constructor, consider marking it protected. That way, clients of the class can't accidentally create a copy, but instances of the class can invoke the copy constructor as needed to implement the clone function. You can use the defaulted version of the copy constructor assuming you aren't doing any explicit resource management. Then, to implement clone, you can do something like this:
virtual unique_ptr<B> clone() override
{
return make_unique<D>(*this);
}
This invokes the object's own (protected) copy constructor, which in turn will invoke the base's (protected) copy constructor, etc.
As a note, there's no need to use CRTP here. Using good old fashioned copy constructors should be all you need.

Related

Deriving from an abstract base with a user-declared dtor and move-support

A user-declared dtor prevents the autogeneration of the move-ctor/-assignment-operator,
but will the autogeneration only be prevented in the class where the dtor has been defined or will the autogeneration be prevented in all derived classes too? I am asking this, because I am using many pure virtual classes which all provide a user-declared dtor. Do I have to upgrade now all this classes to get move-support or will it work still out-of-the-box?
Here is an example of how my scenarios currently looks like:
struct BigData {};
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
};
Now, which of the following variants I have to use to
be sure, that moving will be used like in the following
example?
A a;
std::vector< A > va;
va.push_back( std::move( a ) ); //Should really use move instead of copy
Variant 1: Upgrade base class only
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
BaseA() = default;
BaseA(BaseA&&) = default;
BaseA& operator=(BaseA&&) = default;
BaseA(const BaseA&) = default;
BaseA& operator=(const BaseA&) = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
};
Variant 2: Upgrade derived class only
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
A() = default;
A(A&&) = default;
A& operator=(A&&) = default;
A(const A&) = default;
A& operator=(const A&) = default;
};
Variant 3: Upgrade base class and derived class
struct BaseA
{
virtual void func() = 0;
virtual ~BaseA() = default;
BaseA() = default;
BaseA(BaseA&&) = default;
BaseA& operator=(BaseA&&) = default;
BaseA(const BaseA&) = default;
BaseA& operator=(const BaseA&) = default;
};
struct A : public BaseA
{
BigData _data;
void func() override {}
A() = default;
A(A&&) = default;
A& operator=(A&&) = default;
A(const A&) = default;
A& operator=(const A&) = default;
};
Variant 4: Nothing to do
Not having a move constructor in the base vs. having a deleted move constructor in the base are two different things.
For the first, the derived classes can still go with the rule of zero and have a default generated move constructor created by the compiler.
For the latter, the default move would be also deleted in the derived.
When you have a user defined destructor in your class (or a user defined copy constructor, or a user defined copy assignment operator) the default move operations are not provided, but they are not implicitly deleted. Thus the derived class is still entitled for the default move operations if it follows the rules for having them, without the need to explicitly declare them as =default.
Cpp Reference says:
The implicitly-declared or defaulted move constructor for class T is defined as deleted if any of the following is true:
...
T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors);
...
Note that having a user declared destructor doesn't make a class un-moveable, it just doesn't have a default generated move.

C++ if a class cannot synthesize a move constructor, does it mean that the move constructor is deleted?

In code
class A
{
public:
A() = default;
A(const A& obj) = default;
};
since i define the copy constructor of A, A won't synthesize the move constructor. But i still can write the following code:
A obj;
A obj1 = std::move(obj); // call the copy constructor of A
But if i define class A like this
class A
{
public:
A() = default;
A(const A& obj) = default;
A(A&& obj) = delete;
};
the same code
A obj;
A obj1 = std::move(obj); // error: call to deleted constructor of A
then how to understand the difference between cannot synthesize a move constructor and the move constructor is deleted?

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;
}

`noncopyable` with custom destructor

I need a noncopyable class which has a declared destructor, and naive approach doesn't work: see https://ideone.com/mU8aoc. What's the problem with the destructor, why moving doesn't work the same way as without it? And of course, how to fix it?
For reference, the complete code (same as by the ideone link above):
class noncopyable {
public:
noncopyable(noncopyable &&) noexcept;
noncopyable &operator=(noncopyable &&) noexcept;
protected:
noncopyable() = default;
~noncopyable() = default;
noncopyable(const noncopyable &) = delete;
noncopyable &operator=(const noncopyable &) = delete;
};
class C: noncopyable {
public:
// compiles if this line is uncommented
// C(C&& c);
C() {}
// also compiles if this is commented
~C() {}
};
C a() {
return {};
}
C b() {
return a();
}
int main() {
return 0;
}
For your code to work, class C must be moveable. When it has no declared destructor, it gets a compiler-generated implicit move constructor (and move assignment operator). But when it has a declared ("custom" in your parlance) destructor, the move constructor (and move assignment operator) are no longer provided implicitly. This is for your safety: it is assumed that if you need an explicit destructor you will need explicit move functions as well.
Reference: http://en.cppreference.com/w/cpp/language/move_constructor

Understanding rule of zero

I have a base class, and I do not want to make derived class copyable. In order to make everything explicit I implement it in that way:
class A {
public:
A() = default;
virtual ~A() = default;
A(const A&) = delete;
A(const A&&) = delete;
A& operator=(const A&) = delete;
A& operator=(const A&&) = delete;
virtual void vFun() = 0;
};
class B : public A {
public:
B() = default;
virtual ~B() = default;
B(const B&) = delete;
B(const B&&) = delete;
B& operator=(const B&) = delete;
B& operator=(const B&&) = delete;
virtual void vFun() override {}
};
Is this correct way of doing such things? According to my knowledge and what I have read, the answer is yes, but I would like to be sure before I introduce it into production system.
EDIT
Taking things into conclusion:
1) Almost always move operators should not be deleted. That's because "there's an infinity of things that require movability".
2) For abstract base class, it's safer to allow compiler to generate special member function, and move deletion into derived class if such necessity exists.
Nope, this is completely incorrect.
Firstly, in your quest to make the derived class noncopyable, you have made it non-movable, which renders it nearly useless.
Secondly, there's no reason for A to be noncopyable at all. Each derived class can just make itself noncopyable if it wants to. A is already abstract and cannot be sliced so there's no reason to make A noncopyable.