sfinae 远离破坏者
sfinae away a destructor
我正在实现与 std::vector
非常相似的东西,但在堆栈上使用数组而不是内存分配。
d-tor 调用了一个使用 SFINAE 的函数。
- 如果
value_type
是 POD 则函数体为空。
- 如果
value_type
是正常的class这样的std::string
,函数就有了一个body,并且适当的销毁了所有的数据。
现在,我希望能够将这个新 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::mutex
和std::atomic_flag
的自旋锁。
- 在构造函数中使用函数重载来匹配不同类型。
- 通过
if constexpr
匹配类型并在析构函数中使一些不可转换为指针。
我正在实现与 std::vector
非常相似的东西,但在堆栈上使用数组而不是内存分配。
d-tor 调用了一个使用 SFINAE 的函数。
- 如果
value_type
是 POD 则函数体为空。 - 如果
value_type
是正常的class这样的std::string
,函数就有了一个body,并且适当的销毁了所有的数据。
现在,我希望能够将这个新 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>>);
然而,这是一项全新的功能,最近发生了一些变化(例如,请参阅
析构函数中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::mutex
和std::atomic_flag
的自旋锁。
- 在构造函数中使用函数重载来匹配不同类型。
- 通过
if constexpr
匹配类型并在析构函数中使一些不可转换为指针。