Search code examples
c++initializationatomic

Attempting to reference a deleted function (copy c'tor)


I got this example of spinlock from Anthony Williams, and its something wrong with it (or I had a long day).

#include <atomic>
class spinlock 
{
    std::atomic_flag flag;
public:
    spinlock() : flag(ATOMIC_FLAG_INIT) {}
    void lock() {
        while (flag.test_and_set(std::memory_order_acquire));
    }
    void unlock(){
        flag.clear(std::memory_order_release);
    }
};

spinlock sl;
void f() 
{
    std::lock_guard lc(sl);
}

int main()
{
    f();
}

So this is enough for std::lock_guard to acquire it, but I have a compilation error.

error C2280: 'std::atomic_flag::atomic_flag(const std::atomic_flag &)': attempting to reference a deleted function

To be honest I dont see how spinlock() : flag(ATOMIC_FLAG_INIT) {} is calling a copy constructor.
I use VS2022 with C++20 standard and ATOMIC_FLAG_INIT is defined as following:

#define ATOMIC_FLAG_INIT \
    {}

Is this book example broken, or am I donning something wrong?


Solution

  • ATOMIC_FLAG_INIT can only be used as follows:

    std::atomic_flag v = ATOMIC_FLAG_INIT;
    

    It is unspecified if

    flag(ATOMIC_FLAG_INIT)
    

    will work. If visual studio defines ATOMIC_FLAG_INIT as {} then your code is presumably ending up creating an std::atomic_flag with {} then calling the deleted copy constructor of flag.

    If you're using c++20 you can simply remove the flag initialiser as std::atomic_flag's default constructor now initialises to false.

    Without c++20 I think the only way to do this according to the standard is to use an inline initialiser:

    class spinlock 
    {
        std::atomic_flag flag = ATOMIC_FLAG_INIT;
    public:
        void lock() {
            while (flag.test_and_set(std::memory_order_acquire));
        }
        void unlock(){
            flag.clear(std::memory_order_release);
        }
    };