Reputation: 10319
In C++, you can declare many things as constexpr
: variables, functions (including member functions and operators), constructors, and since C++1z, also if
statements and lambda expressions. However, declaring a destructor constexpr
results in an error:
struct X {
constexpr ~X() = default; // error: a destructor cannot be 'constexpr'
};
My questions:
constexpr
?constexpr
?~X() = default;
), is it automatically constexpr
?Upvotes: 42
Views: 10497
Reputation: 2272
Since C++20, user-defined destructors can also be constexpr
under certain conditions.
The definition of a constexpr function shall satisfy the following requirements:
- its return type (if any) shall be a literal type;
- each of its parameter types shall be a literal type;
- it shall not be a coroutine ([dcl.fct.def.coroutine]);
- if the function is a constructor or destructor, its class shall not have any virtual base classes;
- its function-body shall not enclose ([stmt.pre])
- a goto statement,
- an identifier label ([stmt.label]),
- a definition of a variable of non-literal type or of static or thread storage duration.
As per the C++17 draft basic.types#10, a class type is a literal type only if it is:
A possibly cv-qualified class type that has all of the following properties:
(10.5.1) - it has a trivial destructor,
(10.5.2) - it is either a closure type, an aggregate type, or has at least one constexpr constructor or constructor template (possibly inherited from a base class) that is not a copy or move constructor,
(10.5.3) - if it is a union, at least one of its non-static data members is of non-volatile literal type
(10.5.4) - if it is not a union, all of its non-static data members and base classes are of non-volatile literal types.
Question 1: Why a destructor cannot be marked as constexpr?
Because only trivial destructors are qualified for constexpr. Following is the relevant section of the draft
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial destructors, and
(5.6) — for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Otherwise, the destructor is non-trivial.
Question 2: If I do not provide a destructor, is the implicitly generated destructor constexpr?
Yes, because implicitly generated destructor is trivial type, so it is qualified for constexpr.
Question 3: If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
Yes, this destructor is user-declared and implicitly-generated and thus it is qualified for constexpr.
I'm not able to find any direct reference that only trivial destructors
are qualified for constexpr
but if the destructor is not trivial then it is for sure that class type is not cv-qualified.
So it kind of implicit as you can't define a destructor
for cv-qualified
class.
Upvotes: 23
Reputation: 15693
Since C++20, a constructor may be marked constexpr
; I don’t know if it says anywhere specifically “a destructor may be constexpr
”, but the draft standard includes the following text in section 9.2.5 paragraph 5:
The definition of a
constexpr
destructor whose function-body is not= delete
shall additionally satisfy the following requirement:
- for every subobject of class type or (possibly multi-dimensional) array thereof, that class type shall have a constexpr destructor.
This also now has a useful function because C++20 also allows new
and delete
in constexpr
contexts, allowing things like vector
and string
to work at compile time without hacks (although I believe C++20 does not actually include changes to the standard library to allow for this, it is possible to implement something with the same API and behaviour as vector
that works completely at compile time).
Upvotes: 5
Reputation: 3029
If what you're looking for is reasoning behind the restriction, have a look at this paper which clearly states that the restriction is artificial - there is no intrinsic property of destructors that prevent them from working in constexpr contexts, and indeed compiler implementors agree that supporting them in constexpr contexts will be trivial to implement.
I guess the C++ standards committee originally placed the restriction in C++11 because they didn't want to deal with destructors at that time and it was easier to just rule them out entirely.
Upvotes: 8
Reputation: 21160
A destructor can't be constexpr
because constexpr
functions can't have side effects and destructors by definition are only useful through side effects. In short, it would be useless to have a destructor that is constexpr
.
A object cannot be constexpr
if its destructor is non-trivial. A defaulted one, if trivial, will be considered constexpr
From [class.dtor]
Each decl-specifier of the decl-specifier-seq of a destructor declaration (if any) shall be
friend
,inline
, orvirtual
.
Missing from it, constexpr
. So you could just take it as: because the standard says soTM
Upvotes: 1
Reputation: 206717
Why a destructor cannot be marked as constexpr?
The C++11 standard is specific about use of constexpr
for consructors and non-static member function. It does not say anything specific about destructor. One may assume that destructors are to be treated as non-static member functions.
constexpr
can be used only for const
member functions. Since a destructor cannot be const
member function, it cannot be qualified as a constexpr
member function.
If I do not provide a destructor, is the implicitly generated destructor
constexpr
.
Since use of
constexpr ~X() = default;
is an error, it makes sense to me that the compiler generated destructor is not a constexpr
function. I can't find anything in the standard to justify my statement. I am guessing.
If I declare a defaulted destructor (
~X() = default;
), is it automaticallyconstexpr
I think not. Once again, I can't find anything in the standard to justify my statement. I am guessing.
FWIW, g++ compiles and builds the following program just fine.
struct X {
constexpr X(int i) : i_(i) {}
~X() = default;
int i_;
};
int main()
{
const X x(10);
}
Upvotes: 4
Reputation: 34658
Reference say's:
constexpr destructors
In most cases, in order to create an object of a type T in a constant expression, the destruction of T must be trivial. However, non-trivial destructors are an important component of modern C++, partly due to widespread usage of the RAII idiom, which is also applicable in constexpr evaluations. Non-trivial destructors could be supported in constant expressions, as follows:
- Allow destructors to be marked as constexpr
- Make defaulted destructors constexpr if they only invoke constexpr destructors
- For constexpr variables, require that evaluating the destructor is a constant expression (except that the object being destroyed may be modified in its own destructor
However, no compelling use cases are known for such a feature, and there would be a non-trivial implementation cost ensuring that destructors are run at the right times.
Upvotes: 2