When to make copy and assign constructors delete in c++11? - c++11

I often see the code that deletes 1)copy ctor and 2)assign ctor.
What is the advantage to do so?
I append sample code.
class A{
...
private:
A(const A& copy) = delete;
A &operator=(const A &assign) = delete;
};

It prevents the class from being copied or assigned.
For example it useful when you have a A class that has a m_p pointer as a member variable which that class owns (A class is responsible for deallocation). In this case you can't safely copy or assign the A class. Because compiler generated copy constructor and assign operator will share the ownership of that pointer and in destructor when you will want to delete pointer you can get
a double-delete or access violation from dereferencing a freed pointer.

If you have a case where you don't want to create copies of your class then you restrict that by making the copy constructor and assignment operator private or mark them as delete.
This is useful when you want to create a config class or a cache where having multiple copies of your class don't make sense.
In standard library, strems are not copy constructable and copy-assignable.

Related

Am getting cannot bind non-const lvalue reference of type ‘Type&’ to an rvalue of type 'Type'

Here's my class
class DriverPoint
{
public:
DriverPoint(){};
DriverPoint (DriverPoint& dp) = default;
DriverPoint(double lat, double lon)
{
_lat = lat;
_lon = lon;
}
double _lat;
double _lon;
};
// main
DriverPoint driverPoint(lat, _long);
vector.push_back(driverPoint);
When i try to compile it am getting
cannot bind non-const lvalue reference of type ‘DriverPoint&’ to an rvalue of type ‘DriverPoint’
When adding an element to std containers it is copied (or moved, in case of an rvalue).
So the call to: vec.push_back(driverPoint) calls internally the copy constructor of DriverPoint.
There are two overloads for vector::push_back
(1) void push_back( const T& value );
(2) void push_back( T&& value );
(1) The new element is initialized as a copy of value.
(2) value is moved into the new element.
Overload 2 is for rvalue reference (so it requires T to be MoveInsertable which in this case means having a move constructor). Note that it is not a forwarding reference here, since T is a template parameter of the class and not of the function.
Your call to push_back goes to the first overload, as the passed parameter (the object driverPoint) is a non-const lvalue which can bind to const lvalue (the parameter const T& expected in overload 1 of push_back). Wait... the copy constructor is not called yet! But, inside vector::push_back there is an attempt to create a copy of T, by sending it to the copy constructor as const, which fails if the copy constructor is missing the 'const' on its parameter.
Step 1 - fixing the copy ctor
Adding of course 'const' to the parameter of the copy constructor would fix this: DriverPoint(const DriverPoint& driverPoint)
However, above fix is not the correct one (wait, it is correct, if you want a copy constructor make sure the parameter you expect is const lvalue ref - but in this case the copy constructor is not required at all...).
Step 2 - removing the copy ctor
The problem with having a copy constructor that is not required is that you are implicitly deleting the default move constructor (and others, following the rule of 5).
Note that the current code doesn't even allow adding an rvalue into the vector: vec.push_back(DriverPoint{}) would also fail with the same compilation error.
If you would fix the copy constructor to: DriverPoint(const DriverPoint& driverPoint) both adding lvalue and adding rvalue to the vector by calling push_back would work, but both would go through the copy ctor and not through move, as you didn't implement move and the default move is implicitly deleted if you declare any single one of the five: destructor, copy ctor, copy assignment, move ctor or move assignment.
If you wish to add copy ctor, e.g. for debugging, but still to preserve the default move operations, add:
DriverPoint(DriverPoint&&) = default; // move ctor
DriverPoint& operator=(DriverPoint&&) = default; // move assignment
Rule of Zero
The best way to go with types that do not require special care for copying or destruction, i.e. do not require user implementation of any of the three: destructor - copy ctor - copy assignment operator, is to go with the Rule of Zero -- do not declare any of the five - which would give you the defaults for all five.
Note that it is a common mistake in actual production code -- declaring a destructor, a copy constructor or an assignment operator, thus implicitly deleting the move operations. This may hurt performance badly in quite a quiet way. Move may be used in many places without the programmer's notice, e.g. by std containers. And if it is implicitly deleted the inner fields of the type (e.g. strings, vectors, etc.) would always be copied, even when can be moved. It is especially common in code base that started before C++11, when move operations were not introduced yet but can be also found in new code.

Difference between explicit default destructor and none [duplicate]

The rules for auto generating special move functions (constructor and assignment operator) in C++11 specify that no destructor can be declared. The logic is presumably that, if you need to do something special in destruction, that a move may not be safe.
However, for proper destructor calls in polymorphism, it is necessary to declare a base classes' destructor as virtual (otherwise deleting an instance of a sub class through a pointer of its base class will not properly chain the destructor).
I'm assuming, then, that even an empty destructor would prevent the compiler from automatically generating a special move functions. As in:
class Base {
virtual ~Base() { }
};
You can, however, default the destructor, as in:
class Base {
virtual ~Base() = default;
}
So question 1: Will this allow the compiler to auto generate special move functions?
There is a problem with the explicit default destructor, however. In at least the case of GCC 4.8.2, the signature is implicitly changed to noexcept. As in:
class Base {
virtual ~Base() = default; // compiler changes to:
// virtual ~Base() noexcept;
}
While I have no problem with noexcept in a destructor, this would break the following "client" code:
class Sub : public Base {
virtual ~Sub(); // this declaration is now "looser" because of no noexcept
}
So question 2 is more to the point: is there a way to allow auto generation of special move functions in C++11 and allow proper destructor chaining to sub classes (as described above), all without breaking subclass ("client") code?
No, a defaulted destructor is still considered user defined, so it will prevent the generation of move operations. Also declare the move operations default-ed to make the compiler generate them.
You need to only declare the move operations as default-ed in the base class. In the derived class, the destructor won't be user defined anymore (unless you explicitly say so), so the move operations won't be deleted.
So what I'd do is the following:
class Base
{
virtual ~Base() = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
// probably need to think about copy operations also, as the move disables them
Base(const Base&) = default;
Base& operator=(const Base&) = default;
};
I highly recommend this talk by the person who contributed probably the most to the move semantics: http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014
Or, if you can get your hands on, you should read the Item 17: Understand special member function generation from Scott Meyers' excellent book Effective Modern C++. This issue is excellently explained.
PS: I think you should think a bit more about your base classes. Most of the time, you should use abstract classes, so there will be no need to copy/move instances of them.
PSS: I think by default destructors are marked noexcept in C++11/14, so not explicitly specifying it shouldn't cause any problems:
Inheriting constructors and the implicitly-declared default
constructors, copy constructors, move constructors, destructors,
copy-assignment operators, move-assignment operators are all
noexcept(true) by default, unless they are required to call a function
that is noexcept(false), in which case these functions are
noexcept(false).

Implicitly-generated constructors with explicit declaration of destructor

The thing is, 4th edition of C++ Programming language says:
In this particular case, if you forgot to delete a copy or move operation, no harm is done. A
move operation is not implicitly generated for a class where the user has explicitly declared a destructor.
Furthermore, the generation of copy operations is deprecated in this case (§44.2.3). This
can be a good reason to explicitly define a destructor even where the compiler would have implicitly
provided one (§17.2.3).
I've tried this code:
#include <iostream>
class Foo {
public:
~Foo() {}
}
int main() {
Foo x;
Foo y(x);
return 0;
}
and there is no errors and exeptions here. I know that copy constructor should be generated implicitly in c++98, but 4th says that copy is deprecated. What does it mean?
My understanding is that an implicitly-declared constructor is not necessary implicitly-defined.
From cppreference :
Implicitly-declared copy constructor
If no user-defined copy constructors are provided for a class type (struct, class, or union), the compiler will always declare a copy constructor as a non-explicit inline public member of its class.
Implicitly-defined copy constructor
If the implicitly-declared copy constructor is neither deleted nor trivial, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used.
The generation of the implicitly-defined copy constructor is deprecated if T has a user-defined destructor or user-defined copy assignment operator.
So in your case, the copy constructor is implicitly-declared but not implicitly-defined if not odr-used, which basically means it is not defined unless required somewhere.
See also : What is the distinction between implicitly-declared and implicitly-defined copy constructors?

Is it valid to return a unique_ptr pointing to a forward declared class?

Following code does not compile with clang-700.1.81 and it's standard library:
#include <memory>
class something;
std::unique_ptr<something> external_function();
std::unique_ptr<something> local_function()
{
auto thing = external_function();
return thing;
}
The diagnostics by clang:
......./include/c++/v1/memory:2626:46: note: in instantiation of member function 'std::__1::unique_ptr.....requested here
_LIBCPP_INLINE_VISIBILITY ~unique_ptr() {reset();}
^
test.cc:10:18: note: in instantiation of member function 'std::__1::unique_ptr<something, std::__1::default_delete<something> >::~unique_ptr' requested here
auto thing = external_function();
^
test.cc:4:7: note: forward declaration of 'something'
class something;
^
I guess it is trying to destroy the unique_ptr after copying it as return value, but is this really necessary? It is going be moved anyways, does it need to check if it can copy before realizing that it is easier to move it?
I could of course do this easily with a naked pointer.
Is there some other way to allow a uniqe_ptr to just "pass through" a translation unit, as shown in the example, without including extra header to get the definition of class something?
------EDIT--------
Also tried with GCC 5.3.0 and gnu libstdc++
Does not compile as well, with similar error messages.
------EDIT----
I think it is just trying to destroy the original thing object.
Thanks to Rudolf for the deleter idea ( a bit messy, but only option for this )
Looking at the library code I found this in unique_ptr's code:
if (__tmp)
__ptr_.second()(__tmp);
where second(_tmp) destructs the object pointed to. Even though it is never called, the compiler needs a definition to compile it. This is silly, but apparently gotta live with it.
From cppreference.com:
std::unique_ptr may be constructed for an incomplete type T, such as to facilitate the use as a handle in the Pimpl idiom. If the default deleter is used, T must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and reset member function of std::unique_ptr. (Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete).
Thus, with a custom deleter you can use a forward declared class if the full declaration is available for the deleter:
#include <memory>
class Foo;
class FooDeleter
{
public:
void operator()(Foo* pInstance);
};
std::unique_ptr<Foo, FooDeleter> pFoo;
class Foo
{
};
void FooDeleter::operator()(Foo* pInstance)
{
delete pInstance;
}

Object state after move semantics

I want to understand move semantics and rvalue reference and the object state after the function call.
For example: I expect that caller fills the list and gets to the constructor argument:
typedef std::list<int> IntList;
class IntHolder {
public:
explicit IntHolder(IntList&& l)
: m_h(l)
{}
private:
IntList m_h;
};
IntList a;
a.push_back(1);
IntHolder holder(a);
// ... is 'a' guaranteed empty (not empty) here?
a is guaranteed to not be empty, because it was never passed as an rvalue reference: it is a declared variable, not a compiler generated temporary, and hence cannot be passed as an rvalue reference.
What's happening here is that a temporary IntList gets copy-constructed from a, and that temporary is passed to your constructor. a itself remains intact.
To make things really complicated: even that temporary is not moved! When you use an rvalue reference, it decays to a normal reference, which happens when you say m_h(l) in your initializer list. If it were otherwise, you would not be able to access l from within your constructor. So, the temporary is copied a second time. You can enforce move semantics by replacing m_h(l) with m_h(std::move(l)).
Whenever you try to invoke move semantics, what happens depends on how the class you are using is written. Thus, there are no language guarantees. The only thing a move constructor needs to make sure is that the destructor will not mess up when it is run. It is simply an error to access an object in any way after invoking move semantics on it.

Resources