Search code examples
c++c++17stdatomic

How to use std::atomic<T>::is_always_lock_free for SFINAE?


How can I use std::atomic<T>::is_always_lock_free with SFINAE? I have a class template MyClass<T> and I would like to switch between 2 implementations, depending on whether std::atomic<T>::is_always_lock_free is true. This is what I have:

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

Now, when I try to create an instance of MyClass<SomeCustomClass>, I get a compiler error:

_Atomic cannot be applied to type 'SomeCustomClass' which is not trivially copyable

It tries to use the template specialization, but instead of using the fallback implementation, it doesn't compile at all. Could someone kindly explain what's wrong here? And how do I get the desired result?


Solution

  • You need to delay checking std::atomic<T>::is_always_lock_free until after you know that T is trivially copyable. Otherwise, atomic<T> is ill-formed.

    For that, there's std::conjunction - which is lazy / short-circuits:

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

    Now, this trait will abort early and yield false_type if T isn't trivially copyable. And if it is trivially copyable, then it's valid to instantiate atomic<T>, so then we check that trait.