C++中使用std::atomic时如何规避拷贝与移动构造函数相关编译报错
C++类含std::atomic成员无法存入std::vector的可行解决方案
编译报错的核心原因是std::atomic<T>删除了拷贝构造/拷贝赋值运算符,导致包含该类型成员的类A被隐式删除了拷贝构造/拷贝赋值,而std::vector<T>::push_back默认需要调用拷贝构造,因此触发编译错误。以下是几种不破坏线程安全的可行解决方案:
方案1:手动实现类A的拷贝/移动语义(最推荐,不改变存储结构)
std::atomic的默认拷贝被删除只是禁止了不安全的按字节拷贝,你可以手动实现拷贝逻辑,拷贝过程中使用原子读load()、原子写store()操作,完全不会破坏原子性:
所谓「封装结构体破坏原子性」是错误操作导致的问题:只要不直接对
std::atomic内存做非法memcpy、直接按字节复制等操作,通过原子接口读写的实现是完全线程安全的。
class A { private: std::atomic<bool> flag; std::atomic<int> value; public: A() = default; // 手动实现拷贝构造 A(const A& other) : flag(other.flag.load(std::memory_order_acquire)), value(other.value.load(std::memory_order_acquire)) {} // 手动实现拷贝赋值 A& operator=(const A& other) { if (this != &other) { flag.store(other.flag.load(std::memory_order_acquire), std::memory_order_release); value.store(other.value.load(std::memory_order_acquire), std::memory_order_release); } return *this; } // 可选实现移动构造,支持移动语义避免拷贝开销 A(A&& other) noexcept : flag(other.flag.load(std::memory_order_acquire)), value(other.value.load(std::memory_order_acquire)) {} };
手动实现后原有push_back逻辑可以正常使用。
方案2:vector存储智能指针,避免A对象拷贝
如果不想修改类A的实现,可以将vector的存储类型改为std::unique_ptr<A>或std::shared_ptr<A>,仅拷贝指针而不触碰A的拷贝逻辑:
class B { private: std::vector<std::unique_ptr<A>> vett; public: B() = default; void add_A(std::unique_ptr<A> a) { vett.push_back(std::move(a)); } // 也可以直接在方法内构造A对象 void add_new_A() { vett.emplace_back(std::make_unique<A>()); } }; // 调用示例 int main() { B b{}; b.add_A(std::make_unique<A>()); return 0; }
该方案的缺点是多了一层堆内存分配开销,适合A对象较大、拷贝成本高的场景。
方案3:原地构造对象避免拷贝
如果不需要存入外部构造好的A对象,仅需要在vector内部生成A实例,可以用emplace_back原地构造,完全不需要调用拷贝/移动构造:
class B { private: std::vector<A> vett; public: B() = default; void add_A() { // 直接在vector分配的内存上构造A,无拷贝开销 vett.emplace_back(); } };
该方案适合不需要外部传入A实例的场景,改动最小性能最高。
内容的提问来源于stack exchange,提问作者HakimLaxe
相关产品推荐
相关产品推荐

