如何使用std :: atomic <t> :: is_always_lock_free用于SFINAE?

时间:2017-10-25 19:31:24

标签: c++ c++17 stdatomic

如何在SFINAE中使用std::atomic<T>::is_always_lock_free?我有一个类模板MyClass<T>,我想在两个实现之间切换,具体取决于std::atomic<T>::is_always_lock_freetrue。这就是我所拥有的:

template<typename T, typename Enable = void>
class MyClass {
// Fallback implementation
};

template<typename T>
class MyClass<T, typename std::enable_if<std::atomic<T>::is_always_lock_free>::type> {
// Optimized implementation using std::atomic
};

现在,当我尝试创建MyClass<SomeCustomClass>的实例时,出现编译错误:

  

_Atomic不能应用于类型'SomeCustomClass',这不是可轻易复制的

它尝试使用模板特化,但不是使用回退实现,它根本不编译。有人可以解释这里有什么问题吗?我如何获得理想的结果?

2 个答案:

答案 0 :(得分:8)

您需要延迟检查std::atomic<T>::is_always_lock_free,直到之后您知道T可以轻易复制。否则,atomic<T>格式不正确。

为此,有std::conjunction - 这是懒惰/短路:

template <typename T>
struct is_lock_free_impl
: std::integral_constant<bool, std::atomic<T>::is_always_lock_free> { };

template <typename T>
using is_lock_free = std::conjunction<
    std::is_trivially_copyable<T>,
    is_lock_free_impl<T>>;

现在,如果false_type不可轻易复制,此特征将提前中止并产生T。如果 是可以轻易复制的,那么实例化atomic<T>是有效的,那么我们检查这个特征。

答案 1 :(得分:1)

在表达式std::atomic<T>::is_always_lock_free中,std::atomic<T>的瞬间失败(对于不可挽回的可复制类型),并且这不是直接上下文,因此编译失败。在这种情况下,您无需实例化std::atomic<T>。如果std::atomic<T>合法,SFINAE会在这里工作,但std::atomic<T>::is_always_lock_free不会。{/ p>

  

那么如何获得理想的结果呢?

使用自定义特征:

#include <atomic>
#include <array>
#include <iostream>

class SomeClass { SomeClass& operator=(const SomeClass&) { return *this; } };

template<typename... T>
struct make_void { typedef void type; };

template<typename... T>
using void_t = typename make_void<T...>::type;

template<typename T, typename = void>
struct IsAlwaysLockFree
{
    static constexpr bool value = false;
};

template<typename T>
struct IsAlwaysLockFree<T, void_t<typename std::enable_if<!std::is_trivially_copyable<T>::value>::type>>
{
    static constexpr bool value = false;
};

template<typename T>
struct IsAlwaysLockFree<T, void_t<typename std::enable_if<std::is_trivially_copyable<T>::value>::type>>
{
    static constexpr bool value = std::atomic<T>::is_always_lock_free;
};

template<typename T, typename Enable = void>
class MyClass {
// Fallback implementation
public:
    MyClass(){ std::cout << "Fallback\n"; }
};

template<typename T>
class MyClass<T, typename std::enable_if<IsAlwaysLockFree<T>::value>::type> {
// Optimized implementation using std::atomic
public:
    MyClass(){ std::cout << "Optimized\n"; }
};

int main()
{
    MyClass<SomeClass> a;
    MyClass<std::array<int, 1024>> b;
    MyClass<int> c;
}

Live on Coliru