Std :: atomic <int>: The difference between x.fetch_add (1) and x ++;
The difference, of course, is not safety = atomicity, which is guaranteed for both methods.
The most important difference that I think is that fetch_add() can take a different memory order argument, while for the increment operator it is always memory_order_seq_cst .
Another obvious difference is that fetch_add() can take not only 1 as an argument, but, on the other hand, operator++ will most likely be implemented using the lock inc statement (although theoretically nothing prevents the compiler from optimizing for fetch_add(1) )
Therefore, in answering your exact question, there is no semantically important difference between x++ and x.fetch_add(1) . doc says :
This function behaves as if atomic :: fetch_add was called with 1 and memory_order_seq_cst as arguments.
C ++ 11
C ++ 11 N3337 project 29.6.5 / 33 "Requirements for operations with atomic types":
CA ::operator++(int) volatile noexcept; CA ::operator++(int) noexcept; Returns: fetch_add (1)
29.6.5 / 2 clarifies C and A :
- a refers to one of the atomic types.
- a C refers to the corresponding non-atomic type
I could not find this explanation, but I believe that Returns: fetch_add(1) implies that fetch_add(1) is called for its side effect, of course.
It's also worth taking a look at the prefix version:
CA ::operator++() volatile noexcept; CA ::operator++() noexcept; Effects: fetch_add (1)
Returns: fetch_add (1) + 1
which indicates that it returns a value of + 1, as an increment of the regular prefix for integers.
Gcc 4.8
libstdC ++ - v3 / include / std / atomic says atomic<int> inherits __atomic_base<int> :
struct atomic<int> : __atomic_base<int> libstd ++ - v3 / include / bits / atomic_base.h implements it as:
__int_type operator++(int) noexcept { return fetch_add(1); } __int_type operator++(int) volatile noexcept { return fetch_add(1); } __int_type operator++() noexcept { return __atomic_add_fetch(&_M_i, 1, memory_order_seq_cst); } __int_type operator++() volatile noexcept { return __atomic_add_fetch(&_M_i, 1, memory_order_seq_cst); } _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept { return __atomic_fetch_add(&_M_i, __i, __m); } _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) volatile noexcept { return __atomic_fetch_add(&_M_i, __i, __m); } I don’t understand why the postfix calls the fetch_add , and the prefix uses the built-in directly, but in the end they all boil down to the GCC internal functions __atomic_fetch_add and __atomic_add_fetch that do the real work.