I want to make some classes use automatically generated constructors, but be non-copyable (but still movable). Currently I'm doing it like this:
class A
{
public:
A() = default;
A(const A&) = delete;
A(A&&) = default;
A& operator=(const A&) = delete;
A& operator=(A&&) = default;
}
I wonder if it's really necessary to be so explicit. What if I wrote it like this:
class A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
}
Would it still work the same? What is the minimal set of defaults and deletes for other cases - non-copyable non-movable class, and class with virtual destructor?
Is there any test code I can use to quickly see which constructors are implicitly created?
This will not work because no default constructor will be automatically created for you. No default constructor will be created because you have declared a copy constructor. It is defined as deleted, but it is user-declared nonetheless, so there is no implicitly defaulted default constructor.
The condensed rules for implicitly created constructors are:
The defaulted move constructor and the defaulted move assignment operator are created implicitly unless you have declared any other of the Big 5 special functions (and unless prevented by non-movable members or bases)
The defaulted default constructor (what a name!) is created implicitly unless you have declared any constructor (and unless prevented by non-default-creatable members or bases)
The defaulted copy constructor and the defaulted copy assignment operator are created unless you have declared the move constructor or the move assignment operator (and unless prevented by non-copyable members or bases)
Is there any test code I can use to quickly see which constructors are
implicitly created?
Yes. For example:
#include <type_traits>
class A
{
public:
A() = default;
A(A&&) = default;
A& operator=(A&&) = default;
};
static_assert(std::is_nothrow_default_constructible<A>::value,
"A must be noexcept default constructible");
static_assert(std::is_nothrow_destructible<A>::value,
"A must be noexcept destructible");
static_assert(!std::is_copy_constructible<A>::value,
"A must not be copy constructible");
static_assert(!std::is_copy_assignable<A>::value,
"A must not be copy assignable");
static_assert(std::is_nothrow_move_constructible<A>::value,
"A must be noexcept move constructible");
static_assert(std::is_nothrow_move_assignable<A>::value,
"A must be noexcept move assignable");
Above I've used _nothrow_ in some of the traits. Remove that part if you want to allow the associated special member to throw an exception.
I would suggest you should rarely need to do this. And in the few circumstances where you do need to do it, explicitly declaring which special functions are deleted and which are defaulted may be no bad thing.
The normal reason why you might need to explicitly define or delete special member functions is if your class is some sort of resource managing class. For example it has owning pointers and there is no way of the compiler knowing what the ownership of that resource is. However, as is described in the Rule of Zero article:
C++ allows us to encapsulate ownership policies into generic reusable
classes. This is the important bit! Most often, our ownership needs
can be catered for by "ownership-in-a-package" classes.
Common "ownership-in-a-package" classes are included in the standard
library: std::unique_ptr and std::shared_ptr. Through the use of
custom deleter objects, both have been made flexible enough to manage
virtually any kind of resource.
So it is very rare for us to need to write out own resource managing class anymore. It should normally be possible to build up a class from from other classes that already have the ownership baked in. And then the default special member functions should be as you expect.
For example, if you have a std::unique_ptr member then your class is implicitly uncopyable or if you have a const member then your class is implicitly unassignable.
That said, if you do need to explicitly make a class uncopyable, #n.m. succinctly outlined the rules on when constructors/assignment operators are implicitly defined and so you need at least:
A() = default;
A(A&&) = default;
A& operator=(A&&) = default;
And I agree with you that C++11 is expressive enough that we don't need boost for this any more.
You can avoid definition of deleted copy c'tor and assignment operator, using
boost noncopyable
this way the intent may be even more explicit and clear, than using the "delete" keyword
you can simply use it like:
#include <boost/utility.hpp>
class A : boost::noncopyable {
public:
A () = default;
A (A&&) = default;
A& operator= (A&&) = default;
};
Related
I am reading the below excerpts from the above book (chapter 15 - Object Oriented Programming Section 15.7.1 Virtual Destructors).
Destructors for base classes are an important exception to the rule of thumb that if a class needs a destructor, it also needs copy and assignment (§13.1.4, p. 504). A base class almost always needs a destructor, so that it can make the destructor virtual.
If a base class has an empty destructor in order to make it virtual, then the fact that the class has a destructor does not indicate that the assignment operator or copy constructor is also needed.
I don't understand the part where it mentioned "if a base class has an empty destructor, so that I can make it virtual". I thought the only way to make it virtual on a base class is to have the keyword "virtual".
Any explanation be great!
The author here considers an empty virtual destructor to be a destructor that is still user-declared (not one that is implicitly-declared as virtual because of a base class destructor) and either defined with an empty body or defaulted:
virtual ~MyClass() = default;
or
virtual ~MyClass() {}
It is impossible to force an implicitly-declared destructor to be virtual (if there isn't already a base class with a virtual destructor) and so either of these are the only possibilities to make the destructor virtual but have it behave the same way that the implicitly-defined (non-virtual) one would. That's why the author says "in order to make it virtual".
The quote is saying that having either of these in a class doesn't indicate that the class semantically needs to define its own copy assignment operator or copy constructor. The destructor still does exactly the same as if it was implicitly-defined and so the implicitly-defined copy operations are likely to still be semantically correct.
A non-empty destructor, e.g. one that performs some non-trivial work with the class (not just something like simple logging etc.) is usually an indication that the implicitly defined copy operations will not be semantically correct for the intended behavior of the class, which is why there is the mentioned rule-of-three saying that in such situations copy operations should be manually defined as well to avoid the unintended semantics.
However, in connection with move semantics there is still a problem with an empty virtual destructor. Declaring any destructor in any way will inhibit implicit declaration of the move constructor and move assignment operator.
As a consequence, if your class has e.g. a std::vector member, it will become inefficient, since it can never be moved, only copied. Usually a class with virtual destructor is used polymorphically and not copied/moved around, but that is not always the case. So it may make sense to still explicitly default all move and copy operations (in line with the rule-of-five) in such a situation:
virtual ~MyClass() = default;
MyClass(const MyClass&) = default;
MyClass(MyClass&&) = default;
MyClass& operator=(const MyClass&) = default;
MyClass& operator=(MyClass&&) = default;
Or (probably more likely), given that the class may be intended to only be used polymorphically, explicitly delete all of them to make it impossible for object slicing to occur unintentionally (see C++ core guidelines C.67):
virtual ~MyClass() = default;
MyClass(const MyClass&) = delete;
MyClass(MyClass&&) = delete;
MyClass& operator=(const MyClass&) = delete;
MyClass& operator=(MyClass&&) = delete;
First, it means that, for a base class Base, you'd likely need a virtual destructor:
class Base {
public:
virtual ~Base() {} // note the empty body
};
This is so that deleting a descendant via base pointer calls the proper ctor and frees memory correctly. Note that copy/assignment is not needed here; at this point, we don't even know if the descendant class is expected to have data members or simply fulfills an interface without any data.
This is, however, only true if you're using naked and some of the smart pointers (e.g. std::unique_ptr<>). When you use std::shared_ptr<>, your destructor is automatically saved so you don't need virtual destructors to keep a std::shared_ptr<Descendant> in a std::shared_ptr<Base> before destroying it.
So on https://en.cppreference.com/w/cpp/language/rule_of_three it says:
Because the presence of a user-defined (or = default or = delete declared) destructor, copy-constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions
So for this class I've done the following
#include <string>
#include <iostream>
class Data {
private:
std::string m_name;
public:
Data() { m_name = "stackman"; }
~Data() = default;
Data(const Data&) = delete;
Data& operator=(const Data&) = delete;
Data(Data&&) = delete;
Data& operator=(Data&&) = delete;
std::string get_name() { return m_name; }
};
int main()
{
Data person;
std::cout << person.get_name() << std::endl;
}
I've seen conflicting resources online saying that if the destructor is set to default and if you don't need the other constructors you don't need to delete or define them. So what's the best course of action here?
If you intend to default the destructor and you do not intend to make the class non-movable or non-copyable explicitly, then you should not declare the destructor at all. There is no benefit to doing that. Follow the rule-of-zero and don't declare any of the special member functions at all.
In some circumstances you need to default the destructor explicitly, specifically if you want to declare it as virtual, but otherwise leave it to behave like the implicit destructor. The issue with that is that this disables the implicit declaration of the move operations. So you should, again assuming that you do not want to intentionally disable move or copy operations, explicitly default all of the special member functions. They will still be defined as deleted if the default implementation wouldn't work. This is also in line with the rule-of-five which you quoted.
If you do this in a non-template class the compiler might warn you about the default member function being deleted if that happens. In that case you can remove or delete the offending member function to silence the warning.
If you do intend to explicitly make the class non-copyable or non-movable, then delete the relevant special member functions and default the rest.
I am trying to delete all the copy/move ctor/assignment operators that are implicitly provided, but why am i still able to explicitly delete the default ctor that was supposed to be implicitly deleted?
I have tried =default-ing all the copy/move ctor/assignment operators implicitly provided only to then be told to actually =delete rather than =default. If my understanding of implicit/explicit is correct, the default ctor should be implicitly deleted if the user provides an explicit copy ctor.
I have the following class:
class A {
public:
A() =delete;
A(const A&):...{;} --> my explicitly defined copy ctor
...
}
I expected the compiler to tell me that i cant =delete an implicitly deleted default ctor, but that is not the case. I am using clang8 to complile.
You simply can delete everything you want, fully independent if it was already (implicit) deleted or not.
It is a good idea to show that you did not want to have a default generated function like operators or constructors and "mark" them as deleted. This helps for clarification of the interface!
But: You still can instantiate your class even if you delete the constructor(s)!
class A {
public:
A() =delete;
A(int) = delete;
int a;
void Print() const { std::cout << a << std::endl; }
};
int main()
{
A a{42};
a.Print();
}
See: https://en.cppreference.com/w/cpp/language/aggregate_initialization
From you comment:
I don't want the next programmer to have to look up the inheritance chain to try and figure out what is not already implicitly deleted.
As long as you define a single constructor and do not explicit enable using of base constructors with:
using X::X;
nobody has the need of looking to the base class constructors at all. As said: It is a good idea to mark "unwanted" methods as delete but there are cases, where that did not help in any case as shown above!
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).
C++0x lets you specify certain functions as defaulted:
struct A {
A() = default; // default ctor
A(A const&) = default; // copy ctor
A(A&&) = default; // move ctor
A(Other); // other ctor
~A() = default; // dtor
A& operator=(A const&) = default; // copy assignment
A& operator=(A&&) = default; // move assignment
};
The implementation of these functions is the same as if the compiler generated them, something that normally happens under most circumstances when you don't declare your own.
A default ctor is not generated if you declare any ctor (any of the others above), so you might need to default it to "bring it back."
However, unless a base or data member precludes them, a class always has a copy and move ctor—and if they are precluded, the default implementation won't work. A class always has a dtor.
Why would you need to explicitly default a copy ctor, move ctor, or destructor? Wouldn't the implicitly generated implementations do the same thing, anyway?
You might need to do this to change their access to non-public or to control which translation unit defines them.
Non-public
Even though these functions are commonly public, you may wish them to be non-public while still desiring the default implementation:
struct A {
protected:
~A();
private:
A();
A(A const&);
A(A&&);
};
// according to N3092, §8.4.2/2, cannot be non-public and defaulted
// in the class definition
A::~A() = default;
A::A() = default;
A::A(A const&) = default;
A::A(A&&) = default;
This class can be default-constructed, copied, and moved, but only by methods and friends of A. This is useful for factories, where construction might be more tightly controlled.
A protected destructor is the second half of public-virtual/protected-nonvirtual guideline for base classes:
Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual.
Definition control
Additionally, defaulted functions can be used to maintain a stable binary interface, since you have control over where the defaulted functions are defined. Defaulted doesn't imply inline, as the implicitly declared versions would be. (In the code above, the defaulted functions must either not be in a header or have the inline specifier added.)
In addition to the functional purposes, I find it useful for clarity. It makes it clear that the structure is meant to be default constructable (or whatever), and that we're using the compiler generated behavior. The more self documenting your code is, the better.