C++ atomic variables cannot be assigned to each other: why? is it possible to?

Viewed 857

I have have been doing some math with std::atomic_llong numbers lately and I have recently gotten to this error.

error: use of deleted function ‘std::atomic<long long int>& std::atomic<long long int>::operator=(const std::atomic<long long int>&)’
  202 |     currquadtickpos = prevquadtickpos;
      |                       ^~~~~~~~~~~~~~~
In file included from /home/$USER/position.cpp:21:
/usr/include/c++/9/atomic:848:15: note: declared here
  848 |       atomic& operator=(const atomic&) = delete;
      |               ^~~~~~~~

Basically, I was trying to take an atomic value and assign it to another atomic value, thus making both atomic variables equal. For some reason, however, this is not allowed.

Why is atomic assignment not allowed?

And then, given the answer to the question above, is there another way to do atomic number assignment?

2 Answers

Direct assignment of atomic types is forbidden for a very good reason. Atomic types have a number of operations that look like regular C++ operations, yet they actually give you more guarantees than those operations normally might. For example, some_atomic++ is an atomic increment (which returns the original value), while some_int++ is just an integer increment with no visibility or atomicity guarantees.

If you do something like atomic2 = atomic1, one might get the impression that this entire expression is atomic, that it is an atomic read/modify/write of two variables. It isn't; it would be two sequential atomic operations: a read of atomic1 and a write of atomic2. Two sequential atomic operations isn't the same as a single atomic operation.

To make it clear exactly what isn't going on, they make you spell it out: atomic2 = atomic1.load(...).

As you can see here, atomics are neither copy-able nor movable. If you want to copy the value from one atomic into another, use something like a.store(b.load()). An example:

#include <atomic>
#include <iostream>

int main() {
    std::atomic_llong a;
    std::atomic_llong b;
    a.store(4);
    b.store(5);
    // prints 4 then 5
    std::cout << a.load() << std::endl;
    std::cout << b.load() << std::endl;
    // the important line
    a.store(b.load());
    // prints 5 then 5 (value got copied)
    std::cout << a.load() << std::endl;
    std::cout << b.load() << std::endl;
    return 0;
}
Related