Search code examples
c++language-lawyerc++20stdatomic

Why is copy assignment of volatile std::atomics allowed?


std::atomic has deleted copy assignment operators. Hence, the following results in a compiler error:

std::atomic<int> a1, a2;
a1 = a2; // Error

I think the motivation for the deleted operators is explained e.g. in this post. So far, so good. But I noticed, that adding volatile causes the code to compile suddenly (live on godbolt):

volatile std::atomic<int> a1, a2;
a1 = a2; // OK

I do not really require volatile variables for my project, so this is just out of curiosity: Is this an oversight in the C++ standard, or is this deliberate (why?)?

Note: I can get a compiler error by hacking the std::atomic definition, either by adding

atomic & operator=(const volatile atomic &) volatile = delete;

or by removing the conversion operator operator T() const volatile noexcept.


Solution

  • This is LWG3633.


    std::atomic<T> has a (deleted) copy assignment operator taking a const atomic<T>& (1), an assignment operator function taking a T (2), and a (non-explicit) conversion function to T (3):

    // (1)
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
    
    // (2)
    T operator=(T) noexcept;
    T operator=(T) volatile noexcept;
    
    // (3)
    operator T() const noexcept;
    operator T() const volatile noexcept;
    

    When the assignment source is a non-volatile std::atomic<T>, both assignment operator functions are viable, but (1) is preferred because it does not require a user-defined conversion on the right operand.

    When the right operand is volatile, (1) is not viable because const atomic<T>& cannot bind to a volatile glvalue, so (2) is chosen.