C++ std::vector<std::atomic>在MSVC与GCC/Clang的编译差异及解决方案
代码笔误说明
你提供的示例代码存在两处明显笔误,实际业务代码请先修正:
- reset函数形参名是
_i但内部使用了未声明的变量n - set函数的形参类型写为
in,应为int
问题1:MSVC编译报错的原因
本质是三大编译器对应的C++标准库实现差异导致:
- 你对
vals = std::vector<std::atomic<int>>(n)的逻辑理解是完全正确的,理论上该操作是vector的移动赋值,仅需要交换vector底层的数组指针、大小、容量三个成员,不需要操作元素本身,不会触发atomic的拷贝构造。 - GCC使用的libstdc++、Clang使用的libc++的vector实现做了更精准的模板约束:只要实际执行路径不需要调用元素的拷贝/移动接口,就不会实例化相关的模板函数,因此可以正常编译。
- MSVC使用的Dinkumware STL实现的vector移动赋值运算符存在实现缺陷:模板实例化阶段会提前检查元素类型是否支持拷贝/移动,即使实际执行路径不会触发相关操作,也会因为检测到
std::atomic的拷贝构造是已删除函数而报错。 - 另外你提到的“C不能使用存储atomic的vector”的说法是错误的,C标准从未有过该限制,仅要求调用对应接口时满足元素的类型要求即可,你的原有用法在逻辑上是符合标准的。
问题2:可移植的实现方案
提供两种和原有逻辑完全一致、无额外开销的方案,都可跨三大编译器正常运行:
方案1:改用swap替换vector赋值
vector的swap操作仅交换内部指针,完全不涉及元素的拷贝或移动,所有STL实现都支持该操作,仅需要修改reset函数即可,其余接口逻辑完全不变:
void reset(int n) { // 构造临时vector,默认初始化n个atomic<int> std::vector<std::atomic<int>> temp(n); for (auto& x : temp) { x = 0; } // 交换临时对象和原有对象的底层数据 vals.swap(temp); }
方案2:改用std::unique_ptr动态数组替换vector
因为你本身不需要vector的动态增删能力,仅将其作为固定长度数组使用,用std::unique_ptr<std::atomic<int>[]>更轻量,也完全不会触发atomic的拷贝逻辑,内存布局和vector完全一致,都是连续存储的atomic变量,性能无损失,兼容性最好:
class threadsafe_vals{ std::unique_ptr<std::atomic<int>[]> vals; int m_size = 0; public: threadsafe_vals(int n) : m_size(n), vals(std::make_unique<std::atomic<int>[]>(n)) { for (int i = 0; i < m_size; i++) { vals[i] = 0; } } void reset(int n) { m_size = n; vals = std::make_unique<std::atomic<int>[]>(n); for (int i = 0; i < m_size; i++) { vals[i] = 0; } } int get(int i) { return vals[i]; } void set(int i, int val) { vals[i] = val; } };
内容的提问来源于stack exchange,提问作者user3353819
相关产品推荐
相关产品推荐

