std::set_difference报错:const Thing无赋值运算符问题咨询
首先,你的代码存在几个关键问题,导致编译错误和逻辑不符合预期,我会逐个拆解并给出修复方案:
1. 编译错误的直接原因:std::set的迭代器是const的
你尝试将std::set_difference的结果直接写入differenceResult(一个std::set<Thing>)的迭代器,但std::set的元素是不可修改的(迭代器指向const Thing)——因为set需要保证内部元素的有序性,不允许直接覆盖赋值。std::set_difference的输出迭代器需要指向可写入的位置,因此直接用differenceResult.begin()会触发赋值给const Thing的错误。
解决方法:使用std::inserter来插入结果到临时set中,而不是直接覆盖。std::inserter会调用set的insert方法,完全符合set的特性。
2. 运算符operator<实现错误
你的operator<不仅参数名写错,还搞反了比较逻辑:
bool Thing::operator<(const Thing &Thing) const { return(Other.position<position); }
- 参数名应该是
Other(你写的是Thing,导致Other未定义) - 比较逻辑反了:
std::set需要严格弱序,应该返回this->position < Other.position,这样set会按position从小到大排序,才能保证set_difference正确工作(因为set_difference要求输入序列是有序的)。
修复后的operator<:
bool Thing::operator<(const Thing &Other) const { return this->position < Other.position; }
3. 循环逻辑错误
你在内层循环的每次迭代中都执行newPairWithSet.push_back(...),这会导致每个currentPair被添加pairWithSet.size()次,而不是只添加一次最终的差集结果。应该把push_back放在内层循环结束后。
4. 差集实现的优化与正确性
你的需求是:当前集合与所有其他集合的并集的差集(即当前集合中不在任何其他集合里的元素)。虽然多次调用set_difference(每次减去一个其他集合)最终结果等价于减去并集,但合并所有其他集合到一个临时set后再做一次差集会更高效。
完整修复后的代码示例
#include <vector> #include <set> #include <algorithm> #include <iterator> class Thing { public: Thing() = default; ~Thing() = default; int position; // id bool operator<(const Thing &Other) const; }; bool Thing::operator<(const Thing &Other) const { return this->position < Other.position; } int main() { // 原始vector std::vector<std::pair<Thing, std::set<Thing>>> pairWithSet; // 填充数据(示例) Thing t1{1}, t2{2}, t3{3}; std::set<Thing> s1{{3}, {4}, {5}, {7}}; std::set<Thing> s2{{1}, {3}, {9}}; std::set<Thing> s3{{1}, {2}, {12}}; pairWithSet.emplace_back(t1, s1); pairWithSet.emplace_back(t2, s2); pairWithSet.emplace_back(t3, s3); // 存储最终结果的vector std::vector<std::pair<Thing, std::set<Thing>>> newPairWithSet; for (const auto& currentPair : pairWithSet) { const Thing& currentThing = currentPair.first; std::set<Thing> currentDiff = currentPair.second; // 遍历所有其他集合,逐个减去 for (const auto& otherPair : pairWithSet) { if (otherPair.first.position != currentThing.position) { std::set<Thing> tempDiff; std::set_difference( currentDiff.begin(), currentDiff.end(), otherPair.second.begin(), otherPair.second.end(), std::inserter(tempDiff, tempDiff.begin()) ); currentDiff.swap(tempDiff); // 高效替换当前差集 } } // 添加最终结果到输出vector newPairWithSet.emplace_back(currentThing, currentDiff); } // 验证结果(示例) for (const auto& pair : newPairWithSet) { // 输出逻辑... } return 0; }
替代实现:合并其他集合后做一次差集
如果你的vector很大,这种方式会更高效,因为减少了set_difference的调用次数:
for (const auto& currentPair : pairWithSet) { const Thing& currentThing = currentPair.first; std::set<Thing> allOtherElements; // 合并所有其他集合的元素 for (const auto& otherPair : pairWithSet) { if (otherPair.first.position != currentThing.position) { allOtherElements.insert(otherPair.second.begin(), otherPair.second.end()); } } // 计算当前集合与所有其他元素的差集 std::set<Thing> currentDiff; std::set_difference( currentPair.second.begin(), currentPair.second.end(), allOtherElements.begin(), allOtherElements.end(), std::inserter(currentDiff, currentDiff.begin()) ); newPairWithSet.emplace_back(currentThing, currentDiff); }
关于赋值运算符的疑问
你不需要为Thing类自定义赋值运算符,因为你没有自定义拷贝构造函数,C++编译器会自动生成默认的赋值运算符,足以满足std::set和std::vector的需求。你的编译错误和赋值运算符无关,是前面提到的std::set迭代器const特性导致的。
内容的提问来源于stack exchange,提问作者FooBarExtension

