In C++11, a polymorphic class (one with virtual member methods) should/must have a virtual destructor (so that delete on a base-class pointer does the expected). However, declaring an destructor explicitly deprecates the implicit generation of the copy constructor (though this may not be widely implemented by compilers) and hence also of the default constructor. Thus, for any polymorphic class to not be deprecated it must have these members
virtual ~polymorphic_class() = default;
polymorphic_class() = default;
polymorphic_class(polymorphic_class const&) = default;
explicitly defined, even if they are trivial. Am I correct? (Isn't this annoying?) What is the logic behind this? Is there any way to avoid that?
Am I correct?
Yes, as per ForEveR's post.
Is there any way to avoid that?
Yes. Do this just once by implementing a base for all polymorphic classes (similarly to class Object in Java and D which is the root of all class hierarchies):
struct polymorphic {
polymorphic() = default;
virtual ~polymorphic() = default;
polymorphic(const polymorphic&) = default;
polymorphic& operator =(const polymorphic&) = default;
// Those are not required but they don't harm and are nice for documentation
polymorphic(polymorphic&&) = default;
polymorphic& operator =(polymorphic&&) = default;
};
Then, any class publicly derived from polymorphic will have a implicitly declared and defined (as defaulted) virtual destructor unless you declare one yourself.
class my_polymorphic_class : public polymorphic {
};
static_assert(std::is_default_constructible<my_polymorphic_class>::value, "");
static_assert(std::is_copy_constructible <my_polymorphic_class>::value, "");
static_assert(std::is_copy_assignable <my_polymorphic_class>::value, "");
static_assert(std::is_move_constructible <my_polymorphic_class>::value, "");
static_assert(std::is_move_assignable <my_polymorphic_class>::value, "");
What is the logic behind this?
I can't be sure. What follows are just speculations.
In C++98/03, the Rule of Three says that if a class needs either the copy constructor, the copy assignment operator or the destructor to be user defined, then it probably needs the three to be user defined.
Obeying the Rule of Three is a good practice but this is nothing more than a guideline. The Standard doens't force it. Why not? My guess is that people realized this rule only after the publication of the Standard.
C++11 introduced move constructor and move assignment operator, turning the Rule of Three into the Rule of Five. With benefit of hindsight, the committee wanted to enforce the Rule of Five. The idea was: if either of the five special functions is user declared then the others, but the destructor, won't be implicitly defaulted.
However, the committee didn't want to break virtually every C++98/03 code by enforcing this rule and then, decided to do it only partially:
If either the move constructor or the move assignment operator is user declared then other special functions, but the destructor, will be deleted.
If either of the five special functions is user declared then the move constructor and move assignment operators won't be implicitly declared.
In the case of C++98/03 well formed code, neither a move constructor nor a move assignment operator is ever user declared then, rule 1 doesn't apply. Hence when compiled with a C++11 compliant compiler C++98/03 well formed code doesn't fail to compile as a consequence of this rule. (If it does, it's for some other reasons.)
In addition, under rule 2 the move constructor and move assignment operator are not implicitly declared. This doesn't break C++98/03 well formed code either because they never expected the declaration of move operations anyway.
The deprecation mentioned in the OP and quoted in ForEveR's post suggests a possible enforcement of the Rule of Five by a future Standard. Be prepared!
You are correct, that should be true by standard in future, but now it's only deprecated, so every compiler should support implicitly-declared copy constructor, when destructor is virtual now.
n3376 12.8/7
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class
definition declares a move constructor or move assignment operator, the implicitly declared copy constructor
is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has
a user-declared copy assignment operator or a user-declared destructor.
And it seems to me, that you cannot make any workaround for this.
Related
I have a warning in my C++11 code that I would like to fix correctly but I don't really know how. I have created my own exception class that is derived from std::runtime_error:
class MyError : public std::runtime_error
{
public:
MyError(const std::string& str, const std::string& message)
: std::runtime_error(message),
str_(str)
{ }
virtual ~MyError()
{ }
std::string getStr() const
{
return str_;
}
private:
std::string str_;
};
When I compile that code with clang-cl using /Wall I get the following warning:
warning: definition of implicit copy constructor for 'MyError' is deprecated
because it has a user-declared destructor [-Wdeprecated]
So because I have defined a destructor in MyError no copy constructor will be generated for MyError. I don't fully understand if this will cause any issues...
Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error) has a virtual destructor.
Hence I guess it is better not to remove the virtual destructor but to define the copy constructor. But if I need to define the copy constructor maybe I should also define the copy assignment operator and the move constructor and the move assignment operator. But this seems overkill for my simple exception class!?
Any ideas how to best fix this issue?
You do not need to explicitly declare the destructor in a derived class:
§ 15.4 Destructors [class.dtor] (emphasis mine)
A destructor can be declared virtual (13.3) or pure virtual (13.4); if
any objects of that class or any derived class are created in the
program, the destructor shall be defined. If a class has a base class
with a virtual destructor, its destructor (whether user- or
implicitly-declared) is virtual.
In fact, it might even be detrimental to performance in some cases, as explicitly declaring a destructor will prevent the implicit generation of a move constructor and move assignment operator.
Unless you need to do something in your destructor, the best course of action would be to just omit an explicit declaration of a destructor.
If you do need a custom destructor, and are certain that the default copy ctor, copy assignment operator, move ctor and move assignment operator would do the correct thing for you, it is best to explicitly default them like so:
MyError(const MyError&) = default;
MyError(MyError&&) = default;
MyError& operator=(const MyError&) = default;
MyError& operator=(MyError&&) = default;
Some reasoning on why you're seeing the error, because this used to be perfeclty valid code in C++98:
As of C++11, implicit generation of the copy constructor is declared as deprecated.
§ D.2 Implicit declaration of copy functions [depr.impldec]
The implicit definition of a copy constructor as defaulted is
deprecated if the class has a user-declared copy assignment operator
or a user-declared destructor. The implicit definition of a copy
assignment operator as defaulted is deprecated if the class has a
user-declared copy constructor or a user-declared destructor (15.4,
15.8). In a future revision of this International Standard, these implicit definitions could become deleted (11.4).
The rationale behind this text is the well-known Rule of three.
All quotes below are sourced from cppreference.com: https://en.cppreference.com/w/cpp/language/rule_of_three
Rule of Three
If a class requires a user-defined destructor, a user-defined copy
constructor, or a user-defined copy assignment operator, it almost
certainly requires all three.
The reason why this rule of thumb exists is because the default generated dtor, copy ctor and assignment operator for handling different types of resources (most notably pointers to memory, but also others, like file descriptors and network sockets to name just a couple) rarely do the correct behaviour. If the programmer thought that he needed special handling for the closing of a file handle in the class destructor, he most surely wants to define how this class should be copied or moved.
For completeness, below are the often related Rule of 5, and the somewhat disputed Rule of Zero
Rule of Five
Because the presence of a user-defined 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:
Rule of Zero
Classes that have custom destructors, copy/move constructors or
copy/move assignment operators should deal exclusively with ownership
(which follows from the Single Responsibility Principle). Other
classes should not have custom destructors, copy/move constructors or
copy/move assignment operators.
Now I could get rid of that warning by simply removing the virtual destructor but I always thought that derived classes should have virtual destructors if the base class (in this case std::runtime_error) has a virtual destructor.
You thought wrong. Derived classes will always have virtual destructor if you define one in base, no matter if you create it explicitly or not. So removing destructor would be simplest solution. As you can see in documentation for std::runtime_exception it does not provide it's own destructor either and it is compiler generated because base class std::exception does have virtual dtor.
But in case you do need destructor you can explicitly add compiler generated copy ctor:
MyError( const MyError & ) = default;
or prohibit it making class not copyable:
MyError( const MyError & ) = delete;
the same for assignment operator.
Note: the same happens for much different code but I'm writing it here in case someone gets the same warning.
There was a bug in GCC versions 6.4 - 9.0 where using declarations for base_type's operator= and base_type's ctor in types inherited from base_type which was a class template did not actually create copy/move ctor/operators (ending in very unexpected compiler errors that an object can not be copied/moved).
Since GCC 9.0, the bug is fixed but it creates this warning instead. The warning is wrong and should not appear (the using explicitly declares constructors/operators).
Example code with workarounds and GCC version comparison: https://godbolt.org/z/WgIH4c
GCC bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89381
another GCC bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92145
Origin of this discovery: https://github.com/boostorg/spirit/issues/465
This will make your code compile (but not work) in clang 13:
MyError(const MyError&) {};
MyError(MyError&&) {};
MyError& operator=(const MyError&) {};
MyError& operator=(MyError&&) {};
You will need to fill in the appropriate code for the copy constructor,
but also be aware that you don't need all 4 functions, only the ones that are being called.
Especially in connection with std::vector it is important that types are noexcept movable when possible.
So when declaring a move constructor = default like in
struct Object1
{
Object1(Object1 &&other) = default;
};
std::is_nothrow_move_constructible<Object1>::value will be true as every member (0 here) of Object1 is nothrow-move-constructible, which is answered here.
Yet what happens if the move copy constructor is only declared and then later = default defined like in the following code?
struct Object2
{
Object2(Object2 &&other);
};
Object2::Object2(Object2 &&other) = default;
With g++ 4.9.2 std::is_nothrow_move_constructible<Object2>::value is false and I have to mark both the declaration and the definition as noexcept to make it true.
Now what I am interested in is what the actual rules are.
Especially since Item 22 in Effective Modern C++ (Scott Meyers) seems to give ill advice by suggesting to implement the pimpl-idiom move constructor like I did with Object2.
[dcl.fct.def.default]/p2:
If a function is explicitly defaulted on its first declaration,
it is implicitly considered to be constexpr if the implicit declaration would be, and,
it has the same exception specification as if it had been implicitly declared (15.4).
These rules do not apply if the function is explicitly defaulted on a later declaration, as in your later example, so instead, except for destructors, the function is considered noexcept(false) by default like most other functions.
Since the explicit defaulting can be in a different translation unit - and in the pimpl case, is in a different TU - there's no general way for the compiler to figure out after seeing the class definition only whether the move constructor will throw, unless the function is explicitly defaulted in the class definition (i.e., at its first declaration).
Today I stumbled over a code snippet like this one:
class A
{
A() = default;
A (const A&) = delete;
...
}
I've never seen either the delete or default keyword. Are they part of C++11 std? And what are they used for?
Special member functions can now be defaulted or deleted.
A deleted member function still takes part in overload resolution, but if it gets chosen, the program is ill-formed and compilation stops with a useful diagnostic. This is The Right Way to write things like non-copyable classes, and the user gets a proper error message.
A defaulted member function "does what it should", e.g. a defaulted default constructor default-initializes all bases and members and has empty body; a defaulted copy constructor copies each base and member object, and a defaulted assignment operator assigns each base and member object. If any of those operations aren't allowed (e.g. you have reference members), then the defaulted member function is defined as deleted.
Note that your first declaration-definition A() = default; makes the constructor A::A() user-declared but not user-defined; this is important for the classification of A, e.g. whether it is POD. (And notice that this is different from struct A { A(); }; A::A() = default; which is user-defined.)
Another nice consequence is the clarification of implicitly generated things: If you don't write certain functions yourself at all (like copy constructors), one gets implicitly declared for you. When the implicitly-declared one is odr-used, it gets implicitly defined as defaulted, and thus if it's not possible (e.g. if the class has non-copyable members), it actually gets implicitly defined as deleted. So that's generally a neat way of propagating things like non-copyability and non-assignability, at least in terms of the language and the consequent diagnostics.
I read this article from D. Kalev this morning about the new c++11 feature "defaulted and deleted functions", and can't understand the part about performance, namely:
the manual definition of a special member function (even if it's trivial) is usually less efficient than an implicitly-defined one.
By googling to find an answer, I found another article of the same author:
the synthesized constructor and copy constructor enable the implementation to create code that's more efficient than user-written code, because it can apply optimizations that aren't always possible otherwise.
There is no explication, but I read time to time similar claims.
But how is it that writing:
class C { C() = default; };
can be more efficient than
class C { C(){} };
? I though a compiler would be smart enough to detect such situation and optimize that. In other words how is it easier for the compiler to optimize when it sees =default instead of {} (void body function)?
Edit: the question was edited to add the "c++11" tag, but this question remains in c++03 context: just replace class C {C()=default;}; by class C {};, so not really a c++11 specific question.
You ask, how is it that
class C { C() = default; };
can be more efficient than
class C { C(){} };
Well, both constructors do nothing, so it's meaningless to talk about efficiency for that example.
But more generally, in e.g. a copy constructor one can imagine that copying one POD item at a time will not be recognized as optimizable by simple optimizer, whereas with automatic generation it might just do a memcpy. Who knows. It's a Quality of Implementation issue, and I can easily imagine also the opposite.
So, measure, if it matters.
Cheers & hth.,
It makes no sense whatsoever to talk about "manual definition of a special member function (even if it's trivial)", because user-provided special member functions are, by definition, non-trivial. This non-triviality comes into play when using type traits, and also POD-ness, and many optimizations are only possible with trivial or POD types.
A better restatement of the same quote would be:
The defaulted special member functions enable libraries to detect that calls to these functions may be omitted entirely.
From section 12.1 [class.ctor]
A default constructor is trivial if it
is neither user-provided nor deleted
and if:
its class has no virtual
functions (10.3) and no virtual base
classes (10.1), and
no non-static
data member of its class has a
brace-or-equal-initializer, and
all
the direct base classes of its class
have trivial default constructors, and
for all the non-static data members
of its class that are of class type
(or array thereof), each such class
has a trivial default constructor.
Otherwise, the default constructor is
non-trivial.
From section 12.8 [class.copy]:
A copy/move constructor for class X is
trivial if it is neither user-provided
nor deleted and if
class X has no
virtual functions (10.3) and no
virtual base classes (10.1), and
the constructor selected to copy/move
each direct base class subobject is
trivial, and
for each non-static
data member of X that is of class type
(or array thereof), the constructor
selected to copy/move that member is
trivial;
otherwise the copy/move
constructor is non-trivial.
From section 9, [class]:
A trivially copyable class is a class
that:
has no non-trivial copy
constructors (12.8),
has no
non-trivial move constructors (12.8),
has no non-trivial copy assignment
operators (13.5.3, 12.8),
has no
non-trivial move assignment operators
(13.5.3, 12.8), and
has a trivial
destructor (12.4).
A trivial class is a class that has a
trivial default constructor (12.1) and
is trivially copyable. [ Note: in
particular, a trivially copyable or
trivial class does not have virtual
functions or virtual base classes. —
end note ]
Take performance claims "with a grain of salt".
I've heard a high-rated MIT professor make a claim like that for his favorite thing, and the only reason nobody asked him "why" was because he was a high-rated MIT professor.
Such constructors and destructors might have other advantages, but claims about performance (outside of big-O) are seldom even meaningful except in highly contrived circumstances.
To be honest, I can't see it either.
Among other things, I can see why one should use
class C { C() = default; };
that seems to me the same as
class C { };
Or, if other constructors are provided, as:
class C {
C() {}
// other constructors.
};
I fail to see the real problem the author is writing about here.
struct Base{
Base(Base &){} // suppress default constructor
};
struct Derived : Base{
};
int main(){
Derived d;
}
The code shown gives error because the default constructor (implicit) of 'Base' is suppressed. Indeed the standard says in $12.1 "If there is no user-declared constructor for class X, a default constructor is implicitly declared."
There are three things:
a) Does the standard say anywhere that
if the user declared constructor is
present in a class, the default
constructor (implicit) is suppressed. It is bascically the above phrased negatively or is it once again implied :)?
b) Why is it that way?
c) Why the same rules do not apply for the default destructor?
I think that a) is sufficiently clearly implied by your quote.
As for “why” – quite simple: a default constructor is not always meaningful; if there were no way to suppress it, this would weaken C++ substantially.
As for c), a class without destructor (no “default”, just plain destructor) is simply not meaningful.
a) Does the standard say anywhere that
if the user declared constructor is
present in a class, the default
constructor (implicit) is suppressed.
It is bascically the above phrased
negatively or is it once again implied
:)?
Yes, that is the meaning
b) Why is it that way?
Most likely, if you have a user-defined constructor, it means special work needs to be done to initialize the object. It makes sense to disable the implicitly generated default constructor in such a case, because it likely won't do any of the special work.
c) Why the same rules do not apply for the default destructor?
Well, perhaps it would make sense for the language to enforce the "rule of three" (if you define one of copy constructor, assignment operator or destructor, chances are you need to implement all three), but it just doesn't.
Perhaps the rationale is that there are several ways to initialize a class, but assignment and destruction often works the same way (memberwise assignment, run destructor of all members).
The shortest answer is because you declared a constructor for the class Base, no default constructor is created (thus the suppression). You cannot initialize Derived because Derived has no default constructor to call on class Base. (this is because the default constructor of derived that is generated for you can only construct it's parent class with the default constructor)