Reputation:
First, let me make this question more specific.
By saying
check if a type's destructor can be "ignored"
I'm mean
check if a class has no side effects when it's instances die.
What I'm I doing:
I'm writing a garbage collection library for our C++ projects, and I need enhance the performance. If I can detect the passed-in type T
do not have side effects when destroying, I can just check all living objects, and the rest of all are garbage and can be marked as "garbage"(the typical young generation collection technology). But if it has side effects, I have to scan every one of the dying object and run it's destructor.
For example:
struct S1 {
int i;
}; // can be ignored
struct S2 {
int i;
~S2() {
}
}; // can be ignored
struct S3 {
S3() {
std::cout << "S3()" << std::endl;
}
virtual ~S3() {
std::cout << "~S3()" << std::endl;
}
}; // can not be ignored, destructor has side effect
struct S4 {
S3 s3;
}; // can not be ignored, destructor has side effect(calling s3's destructor)
// this is the most tricky one I tried and failed.
struct S5 {
S3 s3;
~S5() {
}
}; // same with S4
struct S6 : public S3 {
};// can not be ignored, super destructor has side effect
struct S7 : public S1 {
};// can be ignored, super destructor does not have side effect
struct S8 {
virtual ~S8() = default;
}; // can be ignored
// which cannot use is_trivially_destructible
struct S9 : public S8 {
}; // can be ignored
I'v tried to combine is_destructible
and is_trivially_destructible
, but these two cannot meet my requirement. Especially the example 4.
What's more: Any compiler specific features which can solve this problem is welcomed.
Upvotes: 1
Views: 953
Reputation: 19607
The std::is_trivially_destructible
type trait is what you need. Look here. The only case in which the output differs from the desired is S2
. Unfortunately, C++ lacks the constructions to view an empty user-defined destructor as trivial.
Edit:
There is no way to exclude the condition upon virtual
ness from the trait above, so the only option left is to make your own trait. That requires doing extra work on the user side, nothing out-of-the-box. There are several ways of distinguishing user-defined types with a custom predicate:
by the presence of inheritance from a certain type:
class my_class : private can_be_forgotten_tag { ... };
template <typename T>
using can_be_forgotten_v =
std::is_trivially_destructible<T> ||
std::is_base_of<can_be_forgotten_tag, T>;
by the presence of a member (e.g. type alias - tag - how transparent comparators are defined)
by template specializations (not exclusive to 1. and 2.):
template <typename T>
struct can_be_forgotten : std::is_trivially_destructible<T> {};
template <>
can_be_forgotten<S8> : std::true_type {};
More importantly, the question that should have been asked first is whether this is not already something that any compiler does for you automatically (producing the same code with or without destructor calls). That, it is best to check yourself.
P.S. I hope we were considering placement-new the whole time, because otherwise there would be memory leaks.
Upvotes: 4