Do I need to respect the rule of five here? - c++

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.

Related

Why would you =delete implicitly deleted default constructors and what is the point?

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!

Must a c++ interface obey the rule of five?

What is the correct way to declare instantiation methods when defining an interface class?
Abstract base classes are required to have a virtual destructor for obvious reasons. However, the following compilation warning is then given: "'InterfaceClass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move
assignment operator", which is the 'rule of five'.
I understand why the 'rule of five' should be obeyed in general, but is it still applicable for an abstract base class or interface?
My implimentation is then:
class InterfaceClass
{
// == INSTANTIATION ==
protected:
// -- Constructors --
InterfaceClass() = default;
InterfaceClass(const InterfaceClass&) = default;
InterfaceClass(InterfaceClass&&) = default;
public:
// -- Destructors --
virtual ~InterfaceClass() = 0;
// == OPERATORS ==
protected:
// -- Assignment --
InterfaceClass& operator=(const InterfaceClass&) = default;
InterfaceClass& operator=(InterfaceClass&&) = default;
// == METHODS ==
public:
// Some pure interface methods here...
};
// == INSTANTIATION ==
// -- Destructors --
InterfaceClass::~InterfaceClass()
{
}
Is this correct? Should these methods be = delete instead? Is there some way of declaring the destructor to be virtual pure whilst also somehow remaining default?
Even if I declare the destructor as: virtual ~InterfaceClass() = default;, if I do not explicitly default the other four then I will get the same compiler warning.
Tl;dr: What is the correct way to satisfy the 'rule of five' for an interface class as the user must define a virtual destructor.
Thanks for your time and help!
Is this correct? Should these methods be = delete instead?
Your code seems correct. The need of defining special copy/move member functions as default and protected comes clear when you try to copy a derived class polymorphycally. Consider this additional code:
#include <iostream>
class ImplementationClass : public InterfaceClass
{
private:
int data;
public:
ImplementationClass()
{
data=0;
};
ImplementationClass(int p_data)
{
data=p_data;
};
void print()
{
std::cout<<data<<std::endl;
};
};
int main()
{
ImplementationClass A{1};
ImplementationClass B{2};
InterfaceClass *A_p = &A;
InterfaceClass *B_p = &B;
// polymorphic copy
*B_p=*A_p;
B.print();
// regular copy
B=A;
B.print();
return 0;
}
And consider 4 options for defining special copy/move member functions in your InterfaceClass.
copy/move member functions = delete
With special copy/move member functions deleted in your InterfaceClass, you would prevent polymorphic copy:
*B_p = *A_p; // would not compile, copy is deleted in InterfaceClass
This is good, because polymorphic copy would not be able to copy the data member in the derived class.
On the other hand, you would also prevent normal copy, as the compiler won't be able to implicitly generate a copy assignment operator without the base class copy assignment operator:
B = A; // would not compile either, copy assignment is deleted in ImplementationClass
copy/move special member functions public
With copy/move special member functions as default and public, (or without defining copy/move member functions), normal copy would work:
B = A; //will compile and work correctly
but polymorphic copy would be enabled and lead to slicing:
*B_p = *A_p; // will compile but will not copy the extra data members in the derived class.
copy/move special member functions not defined
If move&copy special member functions are not defined, behavior with respect to copy is similar to 2: the compiler will implicitly generate deprecated copy special members (leading to polymorphic slicing). However in this case the compiler will not implicitly generate move special members, so copy will be used where a move would be possible.
protected copy/move member functions (your proposal)
With special copy/move member functions as default and protected, as in your example, you will prevent polymorphic copy which would otherwise had lead to slicing:
*B_p = *A_p; // will not compile, copy is protected in InterfaceClass
However, the compiler will explicitly generate a default copy assignment operator for InterfaceClass, and ImplementationClass will be able to implicitly generate its copy assignment operator:
B = A; //will compile and work correctly
So your approach seems the best and safest alternative
For destructor, if you want to make it both pure virtual and default, you can default it in implementation:
class InterfaceClass
{
// -- Destructors --
virtual ~InterfaceClass() = 0;
};
InterfaceClass::~InterfaceClass() = default;
It does not make much difference if the destructor is default or empty, though.
Now for the rest of your question.
Typically you should have copy constructor and assignment operator defaulted. This way, they don't prevent making default assignment operators and copy constructor in derived classes. Default implementation is correct, as there's no invariant to copy.
So if you want to implement easily Clone method, deleting copy constructor would harm:
class InterfaceClass
{
virtual InterfaceClass* Clone() = 0;
virtual ~InterfaceClass() = 0;
};
class ImplementationClass : public InterfaceClass
{
public:
// This will not work if base copy constructor is deleted
ImplementationClass(const ImplementationClass&) = default;
// Writing copy constructor manually may be cumbersome and hard to maintain,
// if class has a lot of members
virtual ImplementationClass* Clone() override
{
return new ImplementationClass(*this); // Calls copy constructor
}
};
Note also that default implementation of copy/move constructor would not be accidentally used against intention - as instances of abstract base class cannot be created. So you will always be copying derived classes, and they should define, if copying is legal or not.
However, for some classes making copies totally would not make sense, in this case it may be wise to prohibit copying/assigning in the very base class.
Tl;dr: it depend, but most likely you'd better leave them as default.
In general, if any of the big 3 special functions has none-[trivial/default] definition, the other 2 should be defined. If the 2 special move functions have none-[trivial-default] definition, then you need take care of all 5.
In the case of an interface with a nop defined dtor, you don't need bother defining the rest - unless for other reasons.
Even none-trivial definitions do not nessecitate a redefinition of other functions; only when some sort of resource management(e.g. memory, file, io, sync...) is involved, one need define the big 3(5).

need to define all available constructor forms in c++

I have a class like this:
class A{
private:
init(const std::string& s=""){/*do something*/}
public:
A(){init();}
A(const A&){init();}
A(const std::string& s){init(s);}
};
Does it cover all the forms that constructors may be called?
What I am trying to do is, any object creation must call the init() function at first i.e I need to define all the constructors that may be called implicitly or explicitly.
A(const A&)
once you have defined A(A const&) the compiler will not synthesize any other constructors. The only callable constructors will be the ones you declare.
The same is true of A(A&&), as an aside.
It sounds like you have a slight misunderstanding of C++ constructors.
Under specific circumstances classes have certain constructors implicitly declared with defaulted definitions. To avoid constructors that don't call your init() function does not require you to declare these constructors. You can instead follow certain rules that prevent them from being implicitly declared, or you can define some in-class initialization which ensures that the default definitions do the extra initialization you want.
The default constructor is only implicitly declared as defaulted if there are no user-declared constructors.
A copy constructor is only implicitly declared as defaulted if there are no user-declared move constructors or move-assignment operators (and of course no user-declared copy constructor).
A move constructor is only implicitly declared if there are no user-declared copy constructors, copy assignment operators, move constructors, move assignment operators, or destructors (and if the default definition for a move constructor would be valid).
So you could ensure that object construction always calls your init() function by declaring some subset of members that suppresses implicit declarations of any other constructors.
(This slideshow has a handy reference on the conditions for implicit declarations of the special member functions.)
Following the above rules: Declaring this copy constructor, for example, suppresses the default constructor, the move constructor, and the implicitly declared copy constructor. This therefore ensures that no objects of this class can be constructed without calling the init() member function.
struct S {
S(S const &) { init(); }
void init() {}
};
You can also simply use C++11 in-class initialization, which executes even in default definitions, to ensure init() is called for all construction.
struct S {
int x = init();
int init() { return 10; }
// ... whatever constructors you want to define
};
So long as your user-declared constructors don't explicitly initialize x, then all the constructors, both user-declared and implicitly declared, will call init().
I have a hack that does this, but it isn't the most clear on intent.
class A{
private:
bool init(...){...; return true; }
bool initialized = init(...);
public:
// your constructors.
};
Do keep in mind that the parameters to init have not to be dependent on the parameters of the constructor.
For this case, it might be worth considering using implementation inheritance:
class base {
protected:
base(std::string const &s="") { /* equivalent of your `init` */ }
// Or possibly use overloading:
// base() { /* whatever */ }
// base(std::string const &) { /* whatever */ }
};
class A : public base {
A(const std::string& s) : base(s) {}
A(const A&, const std::string& s) : base(s) {}
};
Since A is derived from base, every ctor for A must invoke base's constructor. If you don't use the member initializer list to pass a parameter, it'll be invoked automatically with the default parameter--but creating an A without invoking the base ctor is essentially impossible.
I think you'd better define the move constructor A(A&&), I'm not sure about the policies on automatically generate constructor of different compilers, though C++11 has the explicit definition.
see more info here

Uncopyable class with automatic default and move constructors

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;
};

What are defaulted methods and how do I use them properly? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What’s the point in defaulting functions in C++11?
C++11 introduced defaulted methods (e.g. void myMethod() = default;).
What does it do to methods (how do methods behave after being defaulted). How do I use them properly (what are its uses)?
There are a number of class members that are considered "special member functions" by the C++ standard. These are:
The default constructor (a constructor that can be called with no parameters).
The copy constructor (a constructor that can be called with one parameter that is the object type as an lvalue reference).
The copy assignment operator (an operator= overload that can be called with one parameter that is the object type as either an lvalue reference or a value).
The move constructor (a constructor that can be called with one parameter that is the object type as an rvalue reference).
The move assignment operator (an operator= overload that can be called with one parameter that is the object type as either an rvalue reference or a value).
The destructor.
These member functions are special in that the language does special things with them on types. Another thing that makes them special is that the compiler can provide definitions for them if you do not. These are the only functions that you can use the = default; syntax on.
The issue is that the compiler will only provide a definition under certain conditions. That is, there are conditions under which a definition will not be provided.
I won't go over the entire list, but one example is what others have mentioned. If you provide a constructor for a type that is not a special constructor (ie: not one of the constructors mentioned above), a default constructor will not be automatically generated. Therefore, this type:
struct NoDefault
{
NoDefault(float f);
};
NoDefault cannot be default constructed. Therefore, it cannot be used in any context where default construction is needed. You can't do NoDefault() to create a temporary. You can't create arrays of NoDefault (since those are default constructed). You cannot create a std::vector<NoDefault> and call the sizing constructor without providing a value to copy from, or any other operation that requires that the type be DefaultConstructible.
However, you could do this:
struct UserDefault
{
UserDefault() {}
UserDefault(float f);
};
That would fix everything, right?
WRONG!
That is not the same thing as this:
struct StdDefault
{
StdDefault() = default;
StdDefault(float f);
};
Why? Because StdDefault is a trivial type. What does that mean? I won't explain the whole thing, but go here for the details. Making types trivial is often a useful feature to have, when you can do it.
One of the requirements of a trivial type is that it does not have a user-provided default constructor. UserDefault has a provided one, even though it does the exact same thing as the compiler-generated one would. Therefore, UserDefault is not trivial. StdDefault is a trivial type because = default syntax means that the compiler will generate it. So it's not user-provided.
The = default syntax tells the compiler, "generate this function anyway, even if you normally wouldn't." This is important to ensure that a class is a trivial type, since you cannot actually implement special members the way the compiler can. It allows you to force the compiler to generate the function even when it wouldn't.
This is very useful in many circumstances. For example:
class UniqueThing
{
public:
UniqueThing() : m_ptr(new SomeType()) {}
UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
UniqueThing &operator =(const UniqueThing &ptr)
{
m_ptr.reset(new SomeType(*ptr)); return *this;
}
private:
std::unique_ptr<SomeType> m_ptr;
};
We must write every one of these functions to make the class copy the contents of the unique_ptr; there's no way to avoid that. But we also want this to be moveable, and the move constructor/assignment operators won't automatically be generated for us. It'd silly to re-implement them (and error-prone if we add more members), so we can use the = default syntax:
class UniqueThing
{
public:
UniqueThing() : m_ptr(new SomeType()) {}
UniqueThing(const UniqueThing &ptr) : m_ptr(new SomeType(*ptr)) {}
UniqueThing(UniqueThing &&ptr) = default;
UniqueThing &operator =(const UniqueThing &ptr)
{
m_ptr.reset(new SomeType(*ptr)); return *this;
}
UniqueThing &operator =(UniqueThing &&ptr) = default;
private:
std::unique_ptr<SomeType> m_ptr;
};
Actually, default can only apply to special methods - i.e. constructors, destructors, assignment operator:
8.4.2 Explicitly-defaulted functions [dcl.fct.def.default]
1 A function definition of the form:
attribute-specifier-seqopt decl-specifier-seqopt declarator = default ;
is called an explicitly-defaulted definition. A function that is explicitly defaulted shall
— be a special member function,
— have the same declared function type (except for possibly differing ref-qualifiers and except that in
the case of a copy constructor or copy assignment operator, the parameter type may be “reference to
non-const T”, where T is the name of the member function’s class) as if it had been implicitly declared,
and
— not have default arguments.
They behave the same as if the compiler generated them and are useful for cases like:
class Foo{
Foo(int) {}
};
here, the default constructor doesn't exist, so Foo f; wouldn't be valid. However, you can tell the compiler that you want a default constructor without the need to implement it yourself:
class Foo{
Foo() = default;
Foo(int) {}
};
EDIT: Pointed out by #Nicol Bolas in the comments, this truly doesn't explain why you'd prefer this over Foo() {}. What I'm guessing is: if you have a class w/ implementations separated in an implementation file, your class definition (if you don't use =default) would look like:
class Foo{
Foo();
Foo(int);
};
What I'm assuming is that the =default is meant to provide an idiomatic way of telling you "we're not doing anything special in the constructor". With the above class definition, the default constructor could very well not value-initialize the class members. With the =default you have that guarantee just by looking at the header.
Using = default on a constructor or copy-constructor means that the compiler will generate that function for you during compile-time. This happens normally when you exclude the respective functions from your class/struct. And in the case where you define your own constructor, this new syntax allows you to explicitly tell the compiler to default-construct the constructor where it normally won't. Take for instance:
struct S {
};
int main() {
S s1; // 1
S s2(s1); // 2
}
The compiler will generate both a copy-constructor and a default-constructor, thus allowing us to instantiate an S object like we did (1) and copy s2 into s1 (2). But if we define our own constructor, we find that we are unable to instantiate in the same way:
struct S {
S(int);
};
int main() {
S s; // illegal
S s(5); // legal
}
This fails because S has been given a custom-defined constructor, and the compiler won't generate the default one. But if we still want the compiler to give us one, we can explicitly convey this using default:
struct S {
S(int);
S() = default;
S(const S &) = default;
};
int main() {
S s; // legal
S s(5); // legal
S s2(s); // legal
}
Note, however, that only certain functions with certain function-signatures can be overloaded. It follows that this will fail because it is neither a copy-constructor nor a default-constructor or a move or assignment operator:
struct S {
S(int) = default; // error
};
We are also able to explicitly define the assignment operator (just like the default/copy-constructor, a default one is generated for us when we do not write out our own). So take for example:
struct S {
int operator=(int) {
// do our own stuff
}
};
int main() {
S s;
s = 4;
}
This compiles, but it won't work for us in the general case where we want to assign another S object into s. This is because since we wrote our own, the compiler doesn't provide one for us. This is where default comes into play:
struct S {
int operator=(int) {
// do our own stuff
}
S & operator=(const S &) = default;
};
int main() {
S s1, s2;
s1 = s2; // legal
}
But technically it's superfluous to define the assignment operator, copy, or move constructor as default when there is no other function "overriding" it as the compiler will provide one for you in that case anyway. The only case in which it won't provide you one is where you define your own one (or variation thereof). But as a matter of preference, asking the compiler to provide a default function using the new aforementioned syntax is more explicit and easier to see and understand if intentionally left out for the compiler.