C++ copy constructor called at return - c++

error: use of deleted function 'A::A(const A&)'
return tmp;
^~~
Why is the copy constructor called only when there is a virtual destructor in A? How to avoid this?
struct B {};
struct A{
std::unique_ptr<B> x;
virtual ~A() = default;
};
A f() {
A tmp;
return tmp;
}

virtual ~A() = default; is a user declared destructor. Because of that, A no longer has a move constructor. That means return tmp; can't move tmp and since tmp is not copyable, you get a compiler error.
There are two ways you can fix this. You can add a move constructor like
struct A{
std::unique_ptr<B> x;
A() = default; // you have to add this since the move constructor was added
A(A&&) = default; // defaulted move
virtual ~A() = default;
};
or you can create a base class that has the virtual destructor and inherit from that like
struct C {
virtual ~C() = default;
};
struct A : C {
std::unique_ptr<B> x;
};
This works because A no longer has a user declared destructor (Yes, C does but we only care about A) so it will still generate a move constructor in A. The important part of this is that C doesn't have a deleted move constructor, it just doesn't have one period, so trying to move it will cause a copy. That means
C's copy constructor is called in A's implicitly generated move constructor since C(std::move(A_obj_to_move_from)) will copy as long as it doesn't have a deleted move constructor.

Related

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?

Why is a "=default" destructor different than the implicitly declared destructor?

So I read this post:
How is "=default" different from "{}" for default constructor and destructor?
Which discusses why:
~Widget() = default;
Isn't the same as:
~Widget() {}
However, it's also true that the "=default" case is different than the implicitly declared case. In some sense, =default doesn't actually give you the default, which is kinda odd.
Consider the following program:
class A
{
public:
A(std::string str)
{
m_str = str;
}
~A() = default;
A(A const& rhs)
{
printf("Got copied\n");
m_str = rhs.m_str;
}
A(A&& rhs)
{
printf("Got moved\n");
m_str = std::move(rhs.m_str);
}
std::string m_str;
};
class B
{
public:
B(std::string test) : m_a(test)
{
}
~B() = default;
A m_a;
};
int main()
{
B b("hello world");
B b2(std::move(b));
return 0;
}
Running this program will print "Got copied", unless you comment out the defaulted ~B() in which case it will print "Got moved". So why is this? I think "=default" is pretty confusing considering both this and the implicitly declared destructor are supposed to produce "trivial destructors".
The implicitly-defined move constructor for B only gets created if
there are no user-declared copy constructors;
there are no user-declared copy assignment operators;
there are no user-declared move assignment operators;
there are no user-declared destructors;
Now when you say ~B() = default;, while you still get the default destructor, it's now also considered user-declared, and thus there won't be an implicitly defined move constructor.

What do I Need to Return an Object with a unique_ptr Member?

Let's say that I have this object:
struct foo {
std::unique_ptr<int> mem;
virtual ~foo() = default;
};
I can no longer return a foo object created in a function:
foo make_foo() {
foo result;
result.mem = std::make_unique<int>({});
return result;
}
As may be indicated I need the destructor to be virtual because this will be a base class. But even if I'm using the default destructor, that's not enough, I still can't return an object created in a function.
I get the error:
error C2280: foo::foo(const foo &): attempting to reference a deleted function
Is there a way to navigate around this issue?
Per [class.copy.ctor]/8
If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if [...]
X does not have a user-declared destructor.
Since
virtual ~foo() = default;
is a user-declared destructor you no longer have a move constructor so it tries to use the copy constructor but can't because that is deleted as you have a non-copyable member.
To get the move constructor back, and to keep the default constructable, you need to add
foo() = default;
foo(foo&&) = default;
foo &operator=(foo &&) = default; // add this if you want to move assign as well
to foo
The reason you have to add foo() = default; when you add foo(foo&&) = default; is that foo(foo&&) = default; is a used-declared constructor and if you have any user-declared constructors then the default constructor is no longer provided.
This is a "hack" but what you could do is move the virtual destructor into another class and then inherit from that. That will give you a virtual destructor in foo without having to declare it and give you the default constructors you want. That would look like
struct make_virtual
{
virtual ~make_virtual() = default;
};
struct foo : make_virtual {
std::unique_ptr<int> mem;
};
Either provide your own copy constructor and a default constructor, convert the member to a shared pointer or provide a move constructor. One possible solution:
struct foo {
unique_ptr<int> mem;
foo() = default;
foo(const foo& copy);
virtual ~foo() = default;
};
foo::foo(const foo& copy) : mem(new int(*copy.mem)) {}
foo make_foo() {
foo result;
result.mem = make_unique<int>();
return result;
}

How to clone an object without copy constructor

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.

`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