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

时间:2018-04-17 00:53:46

标签: c++ optional c++17 template-meta-programming

我正在研究基于libc ++的optional<T>的{​​{1}}实现,它包含这样的联合:

optional

现在这里变得毛茸茸。我希望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的实现都由另一个基类提供,名为let {'1}},另一个 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>的定义,但派生的析构函数将隐式调用基类的析构函数,该析构函数被删除,因此无法编译。

有没有“优雅”的方法来解决这个问题?我知道一个实用的解决方案,即爆炸bool参数:

__optional_destruct_base<T, false>

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

任何不涉及__optional_destruct_base<T, false>::~__optional_destruct_base()的想法?

1 个答案:

答案 0 :(得分:0)

  

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

我现在已经弄明白了!至少我相信这是行得通的。它通过了libc ++的测试套件。

无论我们放置联盟,默认情况下该类的析构函数都会被删除(如果T不是简单的可破坏的话)。无论谁负责工会成员,那个班级必须也负责有条件的微不足道的析构函数。所以我们必须将联合直接保留在__optional_destruct_base<T>内(因为那是我们选择负责析构函数的类)。

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

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();
    }

我们有它!我还不确定是否可以为具有不可破坏的基类的类编写析构函数...但是 可以解决我的限制问题具体案例,没有遇到模板专业化的组合爆炸,所有人都想成为&#34;最祖先的#34;。