summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2024-04-05 00:26:31 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2024-04-05 00:26:31 +0200
commit35690469481a9bbdf8cc3d1e882fc5f82392e909 (patch)
tree8533e0c6b524b9dfd9cb2ea2b5b4a663cce33485
downloadcpp-templates-35690469481a9bbdf8cc3d1e882fc5f82392e909.tar.gz
cpp-templates-35690469481a9bbdf8cc3d1e882fc5f82392e909.zip
dtor: conditional trivial destructible
-rw-r--r--cond_trivial_destr.cc94
1 files changed, 94 insertions, 0 deletions
diff --git a/cond_trivial_destr.cc b/cond_trivial_destr.cc
new file mode 100644
index 0000000..8186e6b
--- /dev/null
+++ b/cond_trivial_destr.cc
@@ -0,0 +1,94 @@
+#if 0
+set -xe
+g++ -std=c++20 -fsyntax-only cond_trivial_destr.cc
+clang++ -std=c++20 -fsyntax-only cond_trivial_destr.cc
+exit 0
+#endif
+
+#include <type_traits>
+
+// Conditional trivial destructible.
+//
+// Since destructors can't have template parameters, std::enable_if can't be
+// used to selectively enable or disable the destructor via SFINAE.
+//
+// If a template class wants to be trivially destructible depending on the
+// template parameter, one has to work around it with some template
+// specialization (pre cpp20). The implementation in the namespace v1 gives an
+// example for that.
+//
+// Starting with cpp20 we can leverage concepts to enable / disable different
+// destructor implementations. The implementations in the namespace v2 gives an
+// example for that.
+
+// -- PRE CPP20 ----------------------------------------------------------------
+
+namespace v1 {
+template <typename T, bool = std::is_trivially_destructible_v<T>>
+struct storage {
+ ~storage() {
+ if (has_val) {
+ val.~T();
+ has_val = false;
+ }
+ }
+
+ union {
+ T val;
+ };
+ bool has_val = false;
+};
+
+template <typename T>
+struct storage<T, true> {
+ union {
+ T val;
+ };
+};
+
+template <typename T>
+struct option : private storage<T> {};
+} // namespace v1
+
+// -- SINCE CPP20 --------------------------------------------------------------
+
+namespace v2 {
+#if __cplusplus >= 202002L
+template <typename T>
+struct option {
+ ~option() = default;
+ ~option()
+ requires(!std::is_trivially_destructible_v<T>)
+ {
+ val.~T();
+ }
+
+private:
+ union {
+ T val;
+ };
+ bool has_value = false;
+};
+#endif
+} // namespace v2
+
+// -- TESTEE -------------------------------------------------------------------
+
+struct trivial {};
+static_assert(std::is_trivially_destructible_v<trivial>, "");
+
+struct non_trivial {
+ ~non_trivial() {
+ }
+};
+static_assert(!std::is_trivially_destructible_v<non_trivial>, "");
+
+// -- TEST ---------------------------------------------------------------------
+
+static_assert(std::is_trivially_destructible_v<v1::option<trivial>>, "");
+static_assert(!std::is_trivially_destructible_v<v1::option<non_trivial>>, "");
+
+#if __cplusplus >= 202002L
+static_assert(std::is_trivially_destructible_v<v2::option<trivial>>, "");
+static_assert(!std::is_trivially_destructible_v<v2::option<non_trivial>>, "");
+#endif