摧毁一个不可破坏的基类?

Destroy a non-destructible base class?

本文关键字:可破坏 基类 一个 摧毁      更新时间:2023-10-16

我正在基于 libc++ 的optional进行optional<T>的实现,其中包含如下联合:

template<class T, bool = is_trivially_destructible_v<T>>
struct __optional_destruct_base {
union {
char __null_state_;
T __val_;
};
// no destructor needed; this class is trivially destructible
};
template<class T>
struct __optional_destruct_base<T, false> {
union {
char __null_state_;
T __val_;
};
// destructor must be user-provided because the union's destructor is deleted
~__optional_destruct_base() {
if (this->__engaged()) __val_.~T();
}
};
template<class T>
class optional : private __optional_destruct_base<T> { ... };

现在这就是它变得毛茸茸的地方。我希望union成员和this->__engaged()的实现都由另一个基类提供,称为__optional_storage_base<T>,以及另一个bool模板非类型参数。我尝试这样做:

template<class T, bool = is_foo_v<T>>
struct __optional_storage_base {
union { char __null_state_; T __val_; };
bool __engaged() const { puts("foo"); return false; }
};
template<class T>
struct __optional_storage_base<T, false> {
union { char __null_state_; T __val_; };
bool __engaged() const { puts("bar"); return false; }
};
template<class T, bool = is_trivially_destructible_v<T>>
struct __optional_destruct_base : __optional_storage_base<T>
{
};
template<class T>
struct __optional_destruct_base<T, false> : __optional_storage_base<T>
{
~__optional_destruct_base() {
if (this->__engaged()) __val_.~T();
}
};
template<class T>
class optional : private __optional_destruct_base<T> { ... };

但这会导致在实例化optional<some_T_which_is_not_trivially_destructible>时编译器错误,因为:

  • T不是微不足道的可破坏的,
  • 因此,__optional_storage_base<T>的工会成员有一个隐式删除的析构函数,
  • 因此__optional_storage_base<T>本身有一个已删除的析构函数,
  • 因此__optional_destruct_base<T, false>有一个基类,其析构函数被删除;
  • 虽然我提供了__optional_destruct_base<T, false>::~__optional_destruct_base()的定义,但派生析构函数将隐式调用基类的析构函数,该析构函数被删除,因此无法编译。

有没有"优雅"的方法可以解决这个问题? 我知道一个实用的解决方案,那就是分解布尔参数:

template<class T, bool = is_trivially_destructible_v<T>, bool = is_foo_v<T>>
struct __optional_combinatorial_explosion_base { ... };
template<class T>
struct __optional_combinatorial_explosion_base<T, false, true> { ... };
template<class T>
struct __optional_combinatorial_explosion_base<T, true, false> { ... };
template<class T>
struct __optional_combinatorial_explosion_base<T, true, true> { ... };

我也有一个关于以某种方式应用 CRTP 的模糊想法,但我还没有弄清楚如何使其工作。

任何不涉及__optional_combinatorial_explosion_base的想法?

我也有一个关于以某种方式应用 CRTP 的模糊想法,但我还没有弄清楚如何做到这一点。

我现在已经想通了!至少我相信这是有效的。它通过了libc++的测试套件。

无论我们把联合放在哪里,默认情况下,该类的析构函数都将被删除(如果T不是可破坏的)。无论谁负责工会成员,该阶级也必须负责有条件的琐碎破坏者。因此,我们必须将联合直接保留在__optional_destruct_base<T>内部(因为这是我们选择负责析构函数的类)。

但是,我们可以成功地拆分__engaged()成员功能的责任!我们首先将析构函数的代码移回具有联合成员的类中:

template<class T, bool = is_trivially_destructible_v<T>>
struct __optional_destruct_base {
union { char __null_state_; T __val_; };
};
template<class T>
struct __optional_destruct_base<T, false> {
union { char __null_state_; T __val_; };
~__optional_destruct_base() {
if (this->__engaged()) __val_.~T();
}
};
template<class T, bool = is_foo_v<T>>
struct __optional_engaged_base : __optional_destruct_base <T>
{
bool __engaged() const { puts("foo"); return false; }
};
template<class T>
struct __optional_engaged_base<T, false> : __optional_destruct_base<T>
{
bool __engaged() const { puts("bar"); return false; }
};
template<class T>
class optional : private __optional_engaged_base<T> { ... };

几乎可以编译,除了析构函数中试图调用__engaged()的行:

~__optional_destruct_base() {
if (this->__engaged()) __val_.~T();  // oops!
}

此处,基类尝试调用仅在派生类中实现的方法。所以我们应用CRTP!除了我们甚至不需要传入模板参数,因为我们知道谁将提供__engaged方法。

~__optional_destruct_base() {
auto *__self = static_cast<__optional_engaged_base<T> *>(this);
if (__self->__engaged()) __val_.~T();
}

我们有它!我仍然不确定是否可以为具有不可破坏基类的类编写析构函数......但是在我的特定情况下,可以解决这一限制,而不会遇到模板专业化的组合爆炸,这些模板专业化都希望成为"最祖先"。