含std::atomic的类存入std::vector的拷贝构造问题求助
std::atomic成员的类存入std::vector的问题 嘿,这个问题本质是std::atomic本身不支持拷贝操作导致的,咱们一步步拆解你的困惑,再给出可行方案:
先搞懂你遇到的三个报错原因
未显式声明拷贝构造时,编译器提示构造函数已被删除
因为std::atomic的拷贝构造函数是被显式删除的(std::atomic(const std::atomic&) = delete;),当你的类没有自定义拷贝构造时,编译器会尝试自动生成,但一旦类里有不可拷贝的成员,自动生成的拷贝构造就会被删除,所以编译器会报错。定义
foo(foo& other)时,编译器提示找不到拷贝构造函数std::vector的很多操作(比如push_back扩容、元素拷贝)会要求使用const引用版本的拷贝构造函数,你只定义了非const引用的版本,编译器找不到符合要求的构造函数,自然报错。定义
foo(const foo& other)时,提示std::atomic无const拷贝构造函数
这里你可能误解了操作逻辑:你不是要拷贝std::atomic对象本身(这确实不允许),而是要拷贝它存储的值。other.atomic是const对象,但std::atomic::load()是const成员函数,能安全读取它的值,然后我们用这个值去初始化新的std::atomic成员——这是完全合法的,因为std::atomic支持用普通值来构造(这不是拷贝构造,是普通构造函数)。之前的报错是因为编译器误以为你在尝试拷贝std::atomic对象,其实写法调整一下就没问题。
可行解决方案
方案一:正确实现拷贝构造与拷贝赋值
如果你的场景需要拷贝对象,就按下面的方式实现:
#include <atomic> #include <vector> class foo { public: std::atomic<int> atomic{0}; // 显式默认构造函数(可选,但如果声明了其他构造,建议显式写出) foo() = default; // 正确的拷贝构造:用原对象atomic的值初始化新的atomic foo(const foo& other) : atomic(other.atomic.load()) {} // 配套的拷贝赋值运算符,保持行为一致 foo& operator=(const foo& other) { if (this != &other) { // 用store原子化写入值 this->atomic.store(other.atomic.load()); } return *this; } }; // 使用示例 int main() { std::vector<foo> vec; foo f1; f1.atomic.store(42); vec.push_back(f1); // 现在可以正常拷贝插入了 return 0; }
这里的关键是:我们不是拷贝std::atomic对象,而是拷贝它存储的数值,用数值构造新的std::atomic成员,完全避开了原子对象的拷贝操作。
方案二:用移动语义替代拷贝(更高效)
如果你的场景不需要拷贝,只需要转移对象所有权,可以利用std::atomic支持移动构造/赋值的特性:
#include <atomic> #include <vector> #include <utility> // 用于std::move class foo { public: std::atomic<int> atomic{0}; // 显式启用移动构造和移动赋值(声明拷贝构造后,编译器不会自动生成移动,所以要手动启用) foo(foo&&) = default; foo& operator=(foo&&) = default; // 如果不需要拷贝,直接删除拷贝构造和赋值 foo(const foo&) = delete; foo& operator=(const foo&) = delete; }; // 使用示例 int main() { std::vector<foo> vec; vec.emplace_back(); // 直接在vector里构造对象,无拷贝无移动 foo f2; f2.atomic.store(100); vec.push_back(std::move(f2)); // 移动插入,转移f2的所有权 return 0; }
这个方案效率更高,因为移动std::atomic不需要读写数值,只是转移内部资源的所有权。
额外注意点
- 不管用拷贝还是移动,
std::vector扩容时都会触发元素的拷贝/移动,如果你需要避免频繁扩容,可以提前用reserve()预留空间。 - 单个
atomic的load/store是原子操作,但整个对象的拷贝/移动过程不是原子的——如果有其他线程同时修改原对象的atomic成员,拷贝得到的是某个瞬时值,但整个拷贝操作本身没有原子性保障,这点在多线程场景下要特别注意。
内容的提问来源于stack exchange,提问作者ZeroZ30o

