带自定义构造函数的非POD结构体能否安全用于std::atomic?
Hey there, let's break down your questions step by step—this is a common pitfall when trying to wrap atomic variables, so let's clarify things clearly:
1. 能不能安全使用std::atomic<my_counters>?
First off, let's get the requirements for std::atomic<T> straight (even with partial C++11 support, these core rules hold):
- The type
Tmust be TriviallyCopyable: this means no custom copy/move constructors, assignment operators, or destructors—all those operations have to be compiler-generated. Your struct has a custom constructor, which makes it non-trivially constructible, but that's not the dealbreaker here. - The bigger problem is size:
std::atomic<T>relies on platform-native atomic instructions, which only support specific sizes (usually 1, 2, 4, or 8 bytes). Yourmy_countershas 3intmembers—assumingintis 4 bytes, that's 12 total bytes, which almost certainly exceeds the maximum size your platform can handle atomically. Even if your compiler let you declarestd::atomic<my_counters>, it would have to fall back to a mutex-based implementation (if supported at all), which is inefficient and not what you're looking for.
2. Will counters.counter1++ be atomic?
Absolutely not. When you wrap the struct in std::atomic, the atomicity only applies to whole-object operations (like counters.load() or counters.store(...)). Directly accessing counters.counter1 and incrementing it bypasses the atomic wrapper entirely—this is equivalent to modifying a regular non-atomic int in multiple threads, which will cause data races and undefined behavior.
3. The right way to encapsulate your counters
Your goal is to group atomic counters for easier management, so keep the atomicity at the member level, not the whole struct:
struct my_counters { std::atomic<int> counter1; std::atomic<int> counter2; std::atomic<int> counter3; // Custom constructor to initialize all atomics my_counters(int c1, int c2, int c3) : counter1(c1), counter2(c2), counter3(c3) {} };
This approach checks all your boxes:
- Each counter is a proper
std::atomic<int>, so operations likecounter1++,counter2--, andcounter3 +=4are all atomic and thread-safe. - You get the custom constructor you need to initialize values upfront.
- Even with partial C++11 support,
std::atomic<int>is one of the most widely implemented parts of the standard—your compiler will almost certainly handle it correctly.
A quick note on the POD confusion
Some old resources claim std::atomic<T> requires POD types, but that's a misunderstanding. The C++11 standard actually requires T to be TriviallyCopyable, not strictly POD. But even if your struct were TriviallyCopyable, the size issue would still make std::atomic<my_counters> a bad choice.
Extra tips for limited C++11 support
If your compiler is really behind (e.g., doesn't support std::atomic<int> constructors), you can fall back to platform-specific atomic APIs:
- On Linux: Use
__sync_fetch_and_addor__sync_sub_and_fetch - On Windows: Use
InterlockedIncrementorInterlockedAdd
But stick withstd::atomicif you can—it's portable and cleaner.
内容的提问来源于stack exchange,提问作者code_fodder

