代码之家  ›  专栏  ›  技术社区  ›  Quuxplusone

销毁不可销毁的基类?

  •  -2
  • Quuxplusone  · 技术社区  · 6 年前

    我正在实施 optional<T> ,基于libc++'s 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 成员和实施 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 不是微不足道的破坏,
    • 因此,工会成员 __可选的\u storage\u base<T> 具有隐式删除的析构函数,
    • 因此 __可选的\u storage\u 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 ?

    1 回复  |  直到 6 年前
        1
  •  0
  •   Quuxplusone    6 年前

    我对如何应用CRTP也有一个模糊的想法,但我还没有弄清楚如何实现这一点。

    我现在明白了!至少我相信这是可行的。它通过了libc++'s测试套件。

    无论我们将联合放在哪里,该类的析构函数都会在默认情况下被删除(如果 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> { ... };
    

    几乎 编译,但析构函数中尝试调用 __已接合() :

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

    就在这里!我仍然不确定是否有可能为具有不可破坏基类的类编写析构函数。。。但是它 在我的具体案例中,可以绕过这一限制,而不会遇到模板专门化的组合爆炸,这些专门化都想成为“最古老的”。