C++20stladvanced
std::atomic_ref (C++20 Addition)
C++20 · The Big Four · Compiler flag: -std=c++20
Explanation
Provides atomic operations on a non-atomic object through a reference. The referenced object must meet alignment requirements. Allows atomic access to data not originally declared as `std::atomic`.
Real-World Scenario
Perform atomic updates on elements of a plain array shared between threads, without paying the storage overhead of `std::atomic` per element.
Quick Snippet
snippet.cpp
#include <atomic>
#include <thread>
int counters[4] = {};
void increment(int index) {
std::atomic_ref<int> ref(counters[index]);
ref.fetch_add(1, std::memory_order_relaxed);
}Full Example
atomic_ref.cpp
// CodeNebula - C++20 Example: std::atomic_ref
// Compile: g++ -std=c++20 -Wall -Wextra -pthread -o example atomic_ref.cpp
#include <atomic>
#include <iostream>
#include <numeric>
#include <thread>
#include <vector>
struct Counters {
int reads = 0;
int writes = 0;
int errors = 0;
};
int main() {
std::cout << "=== std::atomic_ref - atomic access to non-atomic variables ===\n\n";
// atomic_ref provides atomic operations on an existing non-atomic variable
// Useful when you cannot change the type (e.g., in a struct or library)
int counter = 0;
constexpr int iterations = 10000;
constexpr int num_threads = 4;
std::cout << "--- Without atomic_ref (data race) ---\n";
{
int unsafe_counter = 0;
std::vector<std::jthread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back([&unsafe_counter] {
for (int j = 0; j < iterations; ++j) {
++unsafe_counter; // DATA RACE!
}
});
}
for (auto& t : threads) t.join();
std::cout << "Expected: " << (num_threads * iterations)
<< ", Got: " << unsafe_counter << " (may differ due to race)\n";
}
std::cout << "\n--- With atomic_ref (safe) ---\n";
{
counter = 0;
std::vector<std::jthread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back([&counter] {
std::atomic_ref<int> ref(counter);
for (int j = 0; j < iterations; ++j) {
ref.fetch_add(1, std::memory_order_relaxed);
}
});
}
for (auto& t : threads) t.join();
std::cout << "Expected: " << (num_threads * iterations)
<< ", Got: " << counter << " (always correct)\n";
}
std::cout << "\n=== atomic_ref with struct members ===\n";
{
Counters c;
std::vector<std::jthread> threads;
for (int i = 0; i < 4; ++i) {
threads.emplace_back([&c] {
std::atomic_ref<int> reads(c.reads);
std::atomic_ref<int> writes(c.writes);
for (int j = 0; j < 1000; ++j) {
reads.fetch_add(1, std::memory_order_relaxed);
writes.fetch_add(1, std::memory_order_relaxed);
}
});
}
for (auto& t : threads) t.join();
std::cout << "Reads: " << c.reads << ", Writes: " << c.writes << "\n";
}
std::cout << "\n=== atomic_ref properties ===\n";
std::cout << "is_always_lock_free (int): "
<< std::atomic_ref<int>::is_always_lock_free << "\n";
std::cout << "is_always_lock_free (double): "
<< std::atomic_ref<double>::is_always_lock_free << "\n";
std::cout << "required_alignment (int): "
<< std::atomic_ref<int>::required_alignment << "\n";
std::cout << "\n=== compare_exchange with atomic_ref ===\n";
int value = 10;
std::atomic_ref<int> ref(value);
int expected = 10;
bool success = ref.compare_exchange_strong(expected, 20);
std::cout << "CAS(10 -> 20): " << (success ? "success" : "failed")
<< ", value = " << value << "\n";
return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -pthread -o example atomic_ref.cpp