sfinae 远离破坏者

sfinae away a destructor

我正在实现与 std::vector 非常相似的东西,但在堆栈上使用数组而不是内存分配。

d-tor 调用了一个使用 SFINAE 的函数。

现在,我希望能够将这个新 std::vector 用作 constexpr。然而,即使声明了 c-tor constexpr,代码也无法编译,因为 class 具有非平凡的 d-tor。

这是代码的一小部分:

template<typename T, std::size_t SIZE>
class SmallVector{
    constexpr SmallVector() = default;

    ~SmallVector(){
        destructAll_<value_type>();
    }

    // ...

    template<typename X>
    typename std::enable_if<std::is_trivially_destructible<X>::value == true>::type
    destructAll_() noexcept{
    }

};

如果 value_type 是 POD 并保持非 POD 数据类型的功能,我可以做些什么来使 class 成为 constexpr
(当然不是同时)

直到 C+20

不幸的是,无法使用 SFINAE enable/disable 析构函数,也无法使用未来的概念。那是因为 destructos:

  • 无法模板化
  • 不能有参数
  • 不能有 return 类型

您可以做的是专门化整个 class,或者更好的是,创建一个仅包含 construct/destruct 和基本访问权限的基础 class 并对其进行专门化。

template <class T, class Enable = void>
struct X {
    ~X() {}
};

template <class T>
struct X<T, std::enable_if_t<std::is_pod<T>::value>> {
};

static_assert(std::is_trivially_destructible<X<int>>::value);
static_assert(!std::is_trivially_destructible<X<std::vector<int>>>::value);

C++ 20

据我所知,您可以限制析构函数并在一个非常简单而优雅的解决方案中得到您想要的东西:

template<typename T, std::size_t SIZE>
class SmallVector{
public:
    constexpr SmallVector() = default;

    ~SmallVector() requires std::is_trivially_destructible_v<T> = default;

    ~SmallVector()
    {   
    }
};

static_assert(std::is_trivially_destructible_v<SmallVector<int, 4>>);
static_assert(!std::is_trivially_destructible_v<SmallVector<std::string, 4>>);

然而,这是一项全新的功能,最近发生了一些变化(例如,请参阅 ) and the compiler support is still sketchy. gcc compiles this just fine, while clang is confused by the fact that there are two definitions of the destructor godbolt

析构函数中if constexpr的例子。 (需要 C++17)

template<typename Tp, typename TLock>
struct LockedPtr {
private:
    Tp   *m_ptr;
    TLock *m_lk;
    void prelock(std::mutex *mtx) { mtx->lock(); }
    void prelock(std::atomic_flag *atom) { while(atom->test_and_set(std::memory_order_acquire)); }

public:
    LockedPtr(Tp *ptr, TLock *mtx)
    : m_ptr(ptr), m_lk(mtx) {
        prelock(mtx); 
    }

    ~LockedPtr() {
        if constexpr (std::is_same_v<TLock, std::mutex>)
            ((std::mutex *)m_lk)->unlock();
        if constexpr (std::is_same_v<TLock, std::atomic_flag>)
            ((std::atomic_flag *)m_lk)->clear(std::memory_order_release);
    }
};

这些代码是RAII锁定智能指针的一部分,采用普通std::mutexstd::atomic_flag的自旋锁。

  • 在构造函数中使用函数重载来匹配不同类型。
  • 通过 if constexpr 匹配类型并在析构函数中使一些不可转换为指针。