Swapping class with unique_ptr - c++

#include <memory>
#include <algorithm>
using namespace std;
class A {
public:
unique_ptr<int> u;
A(){}
A(const A& other): u(new int(*other.u)){} // If I comment this out, it works.
// A(A&&){} // This does not help.
};
int main() {
A a;
A b = a;
swap(a, b);
}
This code does not work - fails with a wall of template errors saying no matching function for call to ‘swap(A&, A&)’. Why? Deleting the second constructor helps, but I need it in other code. I guessed it could be connected with automatical deleting of some constructors when other are defined, but manually adding move constructor does not help either. How can I fix this?

std::swap() requires its arguments to be move-constructible and move-assignable.
Given:
struct A {
unique_ptr<int> u;
A();
};
A is swappable, thanks to the implicitly defined move-constructor and move-assignment operators.
But, given:
struct A {
unique_ptr<int> u;
A();
A(A const&);
};
By declaring a user defined copy constructor, you're inhibiting the implicit definition of A's move constructor and move assignment operator
(and by having a non-assignable-copyable member, you're inhibiting the implicit generation of A's copy constructor and copy assignment operator).
To make A swappable again, you need to either user-define both (*):
struct A {
unique_ptr<int> u;
A();
A(A const&);
A(A&&);
A& operator=(A&&);
};
or neither, and just add proper copy-assignment:
struct A {
unique_ptr<int> u;
A();
A(A const&);
A& operator=(A const&);
};
but, this will probably defeat your original intent of avoiding deep copies on swap, so you'll probably end up defining all of them anyway.
(*) noexcept specifications omitted just for brevity ...

Related

How to copy a built-in array via copy-constructor?

We know that a built-in array can neither be copied nor be assigned. So If it is a member data of a class/struct/union It is OK to leave the compiler do its magic to copy them:
struct ArrInt5{
ArrInt5() = default;
ArrInt5(ArrInt5 const&) = default; // OK
int a[5];
};
ArrInt5 a, b = a; // OK
Sometimes that is not the case for example if the array holds objects of non-default- constructible objects. in that
case we do need to define our copy-ctor to do the job:
struct Bar{
// deleted default ctor
Bar(int x) : x_(x){}
int x_ = 0;
};
struct Foo{
Foo();
Foo(Foo const&);
Bar arr_[5];
};
Foo::Foo() : arr_{0, 1, 2, 3, 4}
{}
Foo::Foo(Foo const& rhs) : arr_{rhs.arr_[0], rhs.arr_[1], rhs.arr_[2], rhs.arr_[3], rhs.arr_[4]}
{}
As you can see Foo has a built-in array of five objects of type struct Bar that type is not default-constructible so the default ctor and the copy ctor must initialize it (arr_).
The problem is: How could initialize that array if it is of a big size lets say 1 million element? Should I hard copy them element by element? or there's some workaround?
I know so many will recommend me to use the equivalent STL container std::array but I'm not on that topic, I'm asking whether there's a workaround for my built-in array of non default-constructible objects.
The only way to generate this effect for you automatically is to wrap your array in a class type of some kind that has a compiler-generated copy constructor (e.g. such as a defaulted constructor). In your particular example, you could just have Foo(const Foo&) be defaulted:
struct Foo{
Foo();
Foo(Foo const&) = default;
// This generates your:
// "Foo::Foo(Foo const& rhs) : arr_{rhs.arr_[0], rhs.arr_[1], rhs.arr_[2], rhs.arr_[3], rhs.arr_[4]}"
// by the compiler
Bar arr_[5];
};
However the above only works if your copy-logic is trivial. For more complex copying, you can't always have a default copy constructor -- but we can solve this more generally.
If you wrap an array data member of any type/size in a struct or class and make sure it has compiler-generated copy constructors, then copying is easy in a constructor.
For example:
template <typename T, std::size_t N>
struct Array {
...
T data[N];
};
This allows for the simple case of copying:
Array<Bar,1000000> a = {{...}};
Array<Bar,1000000> b = a;
Or the more complex case of copying in a constructor that might require more logic than the compiler-generated ones:
class Foo {
...
Foo(const Foo& other);
...
Array<Bar,100000> arr_;
};
Foo::Foo(const Foo& other)
: arr_{other.arr_}
{
... other complex copy logic ...
}
At which point, congratulations -- you have invented std::array!
std::array was added to the standard library exactly because it was this simple to introduce a library-solution to this problem without having to alter the language itself. Don't shy away from it.

Factory requires only declaration of copy ctor without implementation

I'm experiencing behavior which I don't understand in a copy constructor of derived class.
class A {
A(const A&);
public:
A() = default;
};
class B : public A {
friend class Factory;
B(const int v) : A(), m_test_val(v) {}
public:
int m_test_val;
B(const B&); // no implementation, just declaration
};
class Factory {
public:
static B create(const int v) {
return B(v);
}
};
int main() {
B b = Factory::create(2);
std::cout << b.m_test_val << '\n';
return 0;
}
The behavior I don't understand is a matter of a working copy constructor B::B(const B&); which, however, does not have any implementation.
When I use B::B(const B&) = default; instead, I get an error saying I'm using deleted function (implicitly deleted because of ill-formation) in the return statement of the Factory::create() function (The A::A(const A&) is private and without implementation on purpose).
And of course, when I use B::B(const B&) = delete;, compiler tells me I use a deleted function.
How is it possible that the copy constructor works with no implementation just with declaration?
Note: The example code is based on a much larger code that behaves the same way, hopefully I didn't leave something out.
The actual copy is elided by the compiler, which is allowed since the copy constructor is accessible. The compiler is of course under no obligation to elide this copy and if it didn't I would expect a linker error not finding the implementation of the copy constructor.

How to ensure the move constructor is used

The code below gives the error:
use of deleted function ‘constexpr B::B(const B&)’
now, I know this happens because the copy constructor is (intentionally) implicitly deleted by specifying a move constructor, and that copying the vector causes the calls to the (deleted) copy constructor. I think I also understand why the vector's copy constructor and assignment operator are used. I clearly want to use the move constructor and assignment operator though: move the object, so also move the vector it contains. So, how do I get my move constructor/assignment operator to use the vector's move constructor/assignment operator?
Here is the code:
#include <vector>
class B {
private:
/* something I don't want to copy */
public:
B() {};
B(B&& orig) {/* move contents */};
B& operator=(B&& rhs) {
/* move contents */
return *this;
};
};
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(orig.vec) {};
A& operator=(A&& rhs) {
vec = rhs.vec;
return *this;
};
};
Just call std::move on the vectors in the expression in which you want to move from them:
class A {
private:
vector<B> vec;
public:
A() : vec() {};
A(A&& orig) : vec(std::move(orig.vec)) {};
// ^^^^^^^^^
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
// ^^^^^^^^^
return *this;
};
};
Even though you take in rvalue references, rhs and orig are still lvalues in the function, so you need to call std::move on them.
To ensure that the "move" constructor and assignment operators are called, you need to provide an object of the correct value category. The value category is used to determine which operators and constructors could be used.
Use std::move to change the value category (from the lvalue in this case) to a xvalue (an rvalue, that can be moved from).
// ...
A(A&& orig) : vec(std::move(orig.vec)) {};
A& operator=(A&& rhs) {
vec = std::move(rhs.vec);
return *this;
};
The move does not copy or change the object in any way, it is simply a cast to an rvalue reference of the argument type - hence modifying the value category.
If all the members of your class are the objects of classes with correctly defined move constructor/assignment operator you should better use default move constructor/assignment operator for your class. This would be much easier and less error-prone and ensure the call of move constructor/assignment operator of class members.
In your particular example it would be:
class A
{
private:
vector<B> vec;
public:
A() : vec() {};
A(A&&) = default;
A& operator=(A&&) = default;
};

Code unexpectedly fails to compile. Why?

Take a look a the following code example which uses class uncopiable similar to boost::noncopyable:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) = default;
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {}
Since I wanted to make inner class move only I explicitly specified its move constructor and assignment operator to be default ones but also since I've heard that it's a good practice to specify all of the "special member functions" in such case I inherited it from uncopiable. The problem is that compilation fails with every compiler and something similar to the following error message is displayed (this message is excerpt from the clang one):
/usr/include/c++/v1/memory:1645:31: error: call to implicitly-deleted copy constructor of 'A::B'
...
main.cpp:26:10: note: in instantiation of function template specialization 'std::__1::vector >::emplace_back<>' requested here
main.cpp:19:3: note: copy constructor is implicitly deleted because 'B' has a user-declared move constructor
It could be fixed by removing inheritance (copy operations would still not be created). But writing copy operations to be explicitly deleted inside class after that is also okay.
My questions are: why does it happen? Could it be considered a deficiency of disabling constructors/assignment operators through inheritance of helper classes?
The problem is that your uncopiable class is not moveable. Therefore the default move constructor / assignment operator of the derived class try to use the deleted copy versions.
static_assert(std::is_move_constructible<uncopiable>::value, ""); // fails
static_assert(std::is_move_assignable<uncopiable>::value, ""); // fails
The reason for this is § 12.8 ¶ 9:
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor,
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator, and
X does not have a user-declared destructor.
Declaring a copy operator or assignment operator as deleted still counts as declaring it.
The solution is of course to declare the move operations for uncopiable.
uncopiable(uncopiable&&) noexcept = default;
uncopiable& operator=(uncopiable&&) noexcept = default;
Note that the move operations should usually be declared noexcept. Especially if you want to use the type in a std::vector like in your example.
This compiles ok on MinGw:
#include <vector>
class uncopiable {
using self = uncopiable;
protected:
uncopiable() {}
~uncopiable() {}
uncopiable(const self&) = delete;
self& operator=(const self&) = delete;
};
struct A {
struct B : uncopiable {
using self = B;
B() {
}
B(B&&) {};
self& operator=(B&&) = default;
~B() {
}
};
A() { v.emplace_back(); }
~A() {}
private:
std::vector<B> v;
};
int main () {
A* a = new A();
}

If I delete the copy constructor, do I get no implicit move constructor?

The following wont compile (tried both clang & gcc)
#include <vector>
struct Foo
{
Foo(int a=0) : m_a(a) {}
Foo(const Foo& f) = delete;
// Foo(Foo&& f) = default;
private:
int m_a;
};
int main()
{
std::vector<Foo> foovec;
foovec.emplace_back(44); // might resize, so might move
}
But if I don't delete the copy constructor, or if I default the move constructor,
it will work. So, does deleting copy constructor suppress move constructor,
and what is the rational behind that?
You should see table about special class members. When you set copy constructor as a deleted one move constructor won't be generated automatically.
See more in table:
Source (slides).