问题概述
在实现高并发日志系统时,遇到了一个典型的C++ std::atomic 拷贝构造函数被删除的编译错误:
error: use of deleted function 'logrotation::LogStatistics::LogStatistics(const logrotation::LogStatistics&)'
error: use of deleted function 'std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)'
错误详细分析
错误根本原因
1. std::atomic 的设计原理
std::atomic
类型故意删除了拷贝构造函数和拷贝赋值操作符,这是有意的设计决策:
cpp
template<typename T>
class atomic {
public:
atomic(const atomic&) = delete; // 拷贝构造函数被删除
atomic& operator=(const atomic&) = delete; // 拷贝赋值被删除
// 只允许原子操作
T load() const noexcept;
void store(T value) noexcept;
// ...
};
2. 为什么这样设计?
原子性保证:
- 拷贝操作无法保证原子性
- 拷贝过程中可能发生数据竞争
- 多个原子变量之间的拷贝不是原子操作
示例问题:
cpp
std::atomic<int> a{10};
std::atomic<int> b{20};
// 假设允许这样做(实际不允许)
a = b; // 这个操作不是原子的!
// b的值可能在读取和写入a之间被其他线程修改
3. 具体错误位置分析
问题代码:
cpp
struct LogStatistics {
std::atomic<uint64_t> total_logs{0}; // 原子类型
std::atomic<uint64_t> sampled_logs{0}; // 原子类型
std::atomic<uint64_t> dropped_logs{0}; // 原子类型
// ...
};
// 在函数中返回LogStatistics对象时触发错误
LogStatistics get_statistics() const {
return statistics_; // ❌ 尝试拷贝包含atomic的结构体
}
编译器错误流程:
get_statistics()
尝试返回LogStatistics
对象- 返回操作需要调用拷贝构造函数
LogStatistics
包含std::atomic
成员std::atomic
的拷贝构造函数被删除- 编译器报错:无法合成默认拷贝构造函数
✅ 解决方案实施
方案1: 自定义拷贝构造函数(推荐)
核心思路 :使用原子操作的 load()
方法安全地读取值,这种方案没有拷贝构造aotmic,只是读取aotmic的值而已
cpp
struct LogStatistics {
std::atomic<uint64_t> total_logs{0};
std::atomic<uint64_t> sampled_logs{0};
std::atomic<uint64_t> dropped_logs{0};
std::unordered_map<LogLevel, uint64_t> level_counts;
std::chrono::system_clock::time_point start_time;
// 默认构造函数
LogStatistics() : start_time(std::chrono::system_clock::now()) {}
// ✅ 自定义拷贝构造函数
LogStatistics(const LogStatistics& other)
: total_logs(other.total_logs.load()), // 原子读取
sampled_logs(other.sampled_logs.load()), // 原子读取
dropped_logs(other.dropped_logs.load()), // 原子读取
level_counts(other.level_counts), // 普通拷贝
start_time(other.start_time) {} // 普通拷贝
// ✅ 自定义赋值操作符
LogStatistics& operator=(const LogStatistics& other) {
if (this != &other) {
total_logs = other.total_logs.load(); // 原子写入
sampled_logs = other.sampled_logs.load(); // 原子写入
dropped_logs = other.dropped_logs.load(); // 原子写入
level_counts = other.level_counts;
start_time = other.start_time;
}
return *this;
}
};
方案2: 移除原子性(不推荐)
cpp
// ❌ 不推荐:失去线程安全性
struct LogStatistics {
uint64_t total_logs = 0; // 普通类型,非线程安全
uint64_t sampled_logs = 0; // 普通类型,非线程安全
uint64_t dropped_logs = 0; // 普通类型,非线程安全
// ...
};
方案3: 使用指针或引用(复杂)
cpp
// 返回引用而不是值
const LogStatistics& get_statistics() const {
return statistics_; // 返回引用,避免拷贝
}