【问题标题】:sfinae away a destructorsfinae 离开一个析构函数
【发布时间】:2016-10-17 20:12:09
【问题描述】:

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

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

  • 如果 value_type 是 POD,则函数的主体为空。
  • 如果value_typestd::string这样的普通类,则函数有一个主体并正确销毁所有数据。

现在,我希望能够将这个新的std::vector 用作constexpr。然而,即使 c-tor 被声明为 constexpr,代码也无法编译,因为该类具有非平凡的 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 数据类型的功能,我可以做些什么来使类成为 constexpr
(当然不是同时)

【问题讨论】:

  • 我会改为从 SmallVectorImpl&lt;T, SIZE, is_trivially_destructible&lt;T&gt;::value&gt; 继承,它将有一个主要定义和一个专门用于可简单破坏的类型。两者之间的唯一区别是析构函数。
  • 我的想法类似——一个只读基类和两个继承版本中的所有变异方法。你也可以用你的想法来回答吗,因为我看不到第三个模板“组件”有什么帮助。你的意思是从两个基类继承?
  • 这种类型与std::vector 有多相似,真的吗?你宁愿重新实现std::array吗? Orrrr 是一个具有巨大自动存储阵列和大量新放置的向量吗? :)
  • @LightnessRacesinOrbit - 是学术目的。并且就像带有后推和大量新放置的数组。
  • 尝试使用 if constexpr?

标签: c++ c++14 constexpr sfinae


【解决方案1】:

直到 C+20

不幸的是,没有办法使用 SFINAE 启用/禁用析构函数,也没有未来的概念。那是因为 destructos:

  • 不能模板化
  • 不能有参数
  • 不能有返回类型

你可以做的是专门化整个类,或者更好的是,创建一个只包含构造/破坏和基本访问的基类并专门化它。

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

但是,这是一个全新的功能,最近发生了一些变化(例如,参见 Disable non-templated methods with concepts),编译器支持仍然很粗略。 gcc 编译得很好,而 clang 对析构函数godbolt 有两个定义这一事实感到困惑@

【讨论】:

    【解决方案2】:

    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 匹配类型并使某些内容无法转换为析构函数中的指针。

    【讨论】:

    • 我想你可能发错了问题?
    • 查看 ~LockedPtr() 的例子,通过 if constexpr 匹配类型并使某些内容无法转换为指针
    • 我同意 OP 可以在析构函数中使用 if constexpr,但是发布一个不相关的大类只是为了演示它是非常令人困惑的。
    • @HolyBlackCat 感谢您的建议,已删除不必要的代码。
    猜你喜欢
    • 2015-06-08
    • 2011-01-14
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多