behavior of synthesised move constructor - c++11

I am reading C++ Primer 5th edition and get the following problems. The book lists several cases that a synthesized move operation is defined as deleted. One of which is "Unlike the copy constructor, the move constructor is defined as deleted if the class has a member that defines its own copy constructor but does not also define a move constructor, or if the class has a member that doesn't define its own copy operations and for which the compiler is unable to synthesize a move constructor. Similarly for move-assignment."
and also provide an demo code as following:
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
However, for both gcc 7.2.1 and clang-900.0.37, the code is runnable, is the book wrong?
Here is the complete test code:
#include <iostream>
struct Y {
Y() { std::cout << "Y()" << std::endl; }
Y(const Y&) { std::cout << "Y(const Y&)" << std::endl; }
//Y(Y&&) { cout << "Y(Y&&)" << endl; }
};
// assume Y is a class that defines its own copy constructor but not a move constructor
struct hasY {
hasY() = default;
hasY(hasY&&) = default;
Y mem; // hasY will have a deleted move constructor
};
int main() {
hasY hy, hy2 = std::move(hy); // error: move constructor is deleted
return 0;
}

The book correctly describes the behavior prescribed by the C++11 standard. The prescribed behavior, however, has changed as of C++14, which adopted the resolution of Defect Report #1402 "Move functions too often deleted"

Related

Does emplace_back construct an object in its new location instead of using a move?

From this link it states
For example, in the code that we began with, my_vec.push_back("foo")
constructs a temporary string from the string literal, and then moves
that string into the container, whereas my_vec.emplace_back("foo")
just constructs the string directly in the container, avoiding the
extra move. For more expensive types, this may be a reason to use
emplace_back() instead of push_back(), despite the readability and
safety costs, but then again it may not. Very often the performance
difference just won’t matter
So I decided to try that and this is what i did
class foo
{
public:
int counter;
foo()
{
std::cout << "Regular constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.push_back(foo()); //Regular constructor and Move Constructor
f.emplace_back(foo()); //Regular constructor and Move Constructor
}
I noticed that both push_back and emplace_back behave similarly. I was thinking that emplace_back will only be calling the regular constructor based on what I read since it will be constructed in the vector stack.
vector::emplace_back(Args&&...) takes the arguments of the constructor you want to construct your new object with. In your quoted example this is const char* for the constructor string::string(const char*). In your own code you're forcing the move constructor by passing a temporary object. To default-construct your object in-place use f.emplace_back() without any arguments as the default constructor takes none.
Also to avoid reallocation (potentially more moves that would spoil your test) ensure the vector has space for your two test objects first using f.reserve(2).
Full code:
class foo
{
public:
foo()
{
std::cout << "Default constructor\n";
}
foo(const foo& f)
{
std::cout << "Copy constructor\n";
}
foo(foo&& f)
{
std::cout << "Move constructor\n";
}
};
int main()
{
std::vector<foo> f;
f.reserve(2);
f.push_back(foo());
f.emplace_back();
}
Output is
Default constructor
Move constructor
Default constructor

What does unique_ptr<T>::operator= do in terms of deallocation

I'm having troubles understanding fully the assignment operator for unique_ptr. I understand that we can only move them, due to the fact that copy constructor and assignment operators are deleted, but what if
a unique_ptr which contains already an allocation is overwritten by a move operation? Is the content previously stored in the smart pointer free'd?
#include <iostream>
#include <memory>
class A{
public:
A() = default;
virtual void act() const {
std::cout << "act from A" << std::endl;
}
virtual ~A() {
std::cout << "destroyed A" << std::endl;
}
};
class B : public A {
public:
B() : A{} {}
void act() const override {
std::cout << "act from B" << std::endl;
}
~B() override {
std::cout << "destroyed from B " << std::endl;
}
};
int main() {
auto pP{std::make_unique<A>()};
pP->act();
==================== ! =======================
pP = std::make_unique<B>(); // || std::move(std::make_unique<B>())
==================== ! =======================
pP->act();
return 0;
}
When I do
pP = std::make_unique<B>();
does it mean that what was allocated in the first lines for pP (new A()) is destructed automatically?
Or should I opt for:
pP.reset();
pP = std::make_unique<B>();
Yes, see section 20.9.1, paragraph 4 of the C++11 draft standard
Additionally, u can, upon request, transfer ownership to another unique pointer u2. Upon completion of
such a transfer, the following postconditions hold:
u2.p is equal to the pre-transfer u.p,
u.p is equal to nullptr, and
if the pre-transfer u.d maintained state, such state has been transferred to u2.d.
As in the case of a reset, u2 must properly dispose of its pre-transfer owned object via the pre-transfer
associated deleter before the ownership transfer is considered complete
In other words, it's cleaning up after itself upon assignment like you'd expect.
Yes, replacing the content of a smart pointer will release the previously-held resource. You do not need to call reset() explicitly (nor would anyone expect you to).
Just for the sake of this particular example. It seems polymorphism in your example didn't allow you to draw clear conclusions from output:
act from A
destroyed A
act from B
destroyed from B
destroyed A
So let's simplify your example and make it straight to the point:
#include <iostream>
#include <memory>
struct A {
explicit A(int id): id_(id)
{}
~A()
{
std::cout << "destroyed " << id_ << std::endl;
}
int id_;
};
int main() {
std::unique_ptr<A> pP{std::make_unique<A>(1)};
pP = std::make_unique<A>(2);
}
which outputs:
destroyed 1
destroyed 2
Online
I hope this leaves no room for misinterpretation.

C++ default move operation in "Effective Modern C++"

Item 17: Understand special member function generation.
Move operations are generated only for classed lacking
explicitly declared move operation, copy operations,
or a destructor.
Now, when I refer to a move operation move-constructing
or move-assigning a data member or base class, there
is no guarantee that a move will actually take place.
"Memberwise moves" are, in reality, more like memberwise
move requests, because types that aren't move-enabled(...)
will be "moved" via their copy operations.
However, I can not verify them on my environment.
// compiled
#include <iostream>
using namespace std;
class Base {
public:
~Base() {}
};
int main() {
Base a, c;
Base b(move(a));
c = move(b);
// explicitly destructor does not disable
// default move constuctor and move assignment operator
return 0;
}
class Base {
public:
Base() {}
Base(Base& b) {}
~Base() {}
};
class Num {
private:
Base b;
};
int main() {
Num a, c;
c = move(a); // passed
Num b(move(c)); // error
// explicitly Base::Base(Base& b) disable default move
// move conctructor.
// Num's default move constructor can not find any move
// constructor for member object Base b, which lead to an
// error. Num's default move constructor does not "moved"
// Base b via their copy operations which is declared.
return 0;
}
The first assertion might be vary from different environments, but the second one is almost wrong.
I am very confusing about it.
Please help me out.
class Base {
public:
~Base() {}
};
Because Base has a user-declared destructor, it does not have a move constructor or move assignment operator at all. The lines
Base b(move(a));
c = move(b);
actually call the copy constructor and copy assignment operator, respectively.
class Base {
public:
Base() {}
Base(Base& b) {}
~Base() {}
};
class Num {
private:
Base b;
};
Again, Base has no move constructor at all. However, Num does have an implicitly-declared move constructor, since Num itself does not declare any special member functions. However, it is implicitly defined as deleted, since the default definition would be ill-formed:
Num::Num(Num&& n) : b(std::move(n.b)) {}
// Cannot convert rvalue of type `Base` to `Base&`
// for the copy constructor to be called.
Note that the "move" of b does attempt to use the copy constructor, but it can't.

Run a function when number of references decrease in shared_ptr

I am developing a cache and I need to know when an object expired.
Is possible run a function when the reference counter of a shared_ptr decrease?
std::shared_ptr< MyClass > p1 = std::make_shared( MyClass() );
std::shared_ptr< MyClass > p2 = p1; // p1.use_count() = 2
p2.reset(); // [ run function ] p1.use_count() = 1
You can't have a function called every time the reference count decreases, but you can have one called when it hits zero. You do this by passing a "custom deleter" to the shared_ptr constructor (you can't use the make_shared utility for this); the deleter is a callable object which is responsible for being passed, and deleting, the shared object.
Example:
#include <iostream>
#include <memory>
using namespace std;
void deleteInt(int* i)
{
std::cout << "Deleting " << *i << std::endl;
delete i;
}
int main() {
std::shared_ptr<int> ptr(new int(3), &deleteInt); // refcount now 1
auto ptr2 = ptr; // refcount now 2
ptr.reset(); // refcount now 1
ptr2.reset(); // refcount now 0, deleter called
return 0;
}
You can specify a deleter functor when creating the shared_ptr. The following article show an example use of a deleter:
http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
Not using a vanilla std::shared_ptr, but if you only require customized behaviour when calling reset() (with no arguments), you can easily create a custom adapter:
template <typename T>
struct my_ptr : public std::shared_ptr<T> {
using std::shared_ptr<T>::shared_ptr;
void reset() {
std::shared_ptr<T>::reset(); // Release the managed object.
/* Run custom function */
}
};
And use it like this:
my_ptr<int> p = std::make_shared<int>(5);
std::cout << *p << std::endl; // Works as usual.
p.reset(); // Customized behaviour.
Edit
This answer is meant to suggest a solution to an issue that I didn't think the other answers did address, that is: executing custom behaviour every time when the refcount is decreased by use of reset().
If the issue is simply to make a call upon object release, then use a custom deleter functor as suggested in the answers by #Sneftel and #fjardon.

Inherit copy constructor

Say I have a class A, which provide a copy constructor (accepting another A).
I then have a derived class B of A, which does not provide a copy constructor.
Can I inherit the copy constructor of A into B, alike what's possible for non-copy constructors?
Note: B does not have any state, except the state contained in A.
If you don't need any special behaviour in addition to what the base class's copy constructor provides, you should be fine with simply telling the compiler to use the default copy constructor, which will invoke the base classes copy constructor
#include <iostream>
class A {
public:
int a;
A()=default;
A(const A& other) {
std::cout << "Class A copy constructor called" << std::endl;
a = other.a;
}
};
class B: public A {
public :
int b;
B() = default;
B(const B& other) = default;
};
int main() {
B b1;
b1.a = 1;
b1.b = 2;
B b2(b1);
std::cout << b2.a << " - " << b2.b << std::endl;
}
Output:
Class A copy constructor called
1 - 2
EDIT:
In order to answer your exact question: I believe that the using directive makes all base class constructors visible in the derived class. I'm personally not too fond of this behavior, so I prefer using the default constructors instead of the using directive, but that's just me.

Resources