Insertion Sort实现报错:Song对象无法赋值问题求助
首先,咱们来搞清楚这个错误的核心原因:你的Song类里的所有私有成员都是const修饰的(title_、artist_、callback_)。根据C++的规则,如果一个类包含const成员、引用成员,或者某个成员的拷贝赋值运算符被删除,编译器会自动删除这个类的默认拷贝赋值运算符(operator=)。而你在排序代码里写的*itr = j;和*jtr = i;,本质就是在调用这个被删除的赋值运算符,所以编译器直接抛出了错误。
下面给你两种可行的解决思路,你可以根据业务需求选择:
思路1:移除成员变量的const修饰(最简单直接)
如果你的业务逻辑允许Song对象的属性被修改,那直接把Song类里私有成员的const关键字去掉就行:
private: string title_; // 移除const修饰 string artist_; // 移除const修饰 function<void()> callback_; // 移除const修饰
这样编译器会自动为Song生成默认的拷贝赋值运算符,你原来的交换代码就能正常运行了。
思路2:保持Song不可变,改用链表节点操作
如果业务上要求Song对象是不可修改的(必须保留const成员),那咱们不能用赋值的方式交换元素,得直接操作std::list的节点。这里给你两种实现方式:
方式A:用std::swap交换元素
虽然Song没有拷贝赋值运算符,但只要它有拷贝构造函数(你的Song有公开构造函数,编译器会生成默认拷贝构造,且成员都支持拷贝),std::swap就能正常工作(它会通过拷贝构造创建临时对象完成交换)。同时,你原来的插入排序逻辑存在小问题,插入排序的核心是把当前元素和前一个元素依次比较,直到找到正确位置,修正后的代码如下:
// 修正后的插入排序代码 list<Song> newSongList = songs_; // 假设从原列表复制得到待排序列表 for (auto itr = next(newSongList.begin()); itr != newSongList.end(); ++itr) { auto jtr = itr; while (jtr != newSongList.begin()) { auto prevJtr = prev(jtr); cout << prevJtr->GetTitle() << " " << jtr->GetTitle() << endl; // 如果前一个元素不应该排在当前元素前面,就交换 if (!Song::CompareTitle(*prevJtr, *jtr)) { cout << "Swap True (" << prevJtr->GetTitle() << "," << jtr->GetTitle() << " )" << endl; swap(*prevJtr, *jtr); jtr = prevJtr; } else { break; // 找到正确位置,停止向前比较 } } }
方式B:用std::list::splice移动节点(更高效)
std::list的splice方法可以直接移动节点,不需要拷贝或赋值,非常适合处理不可变对象的排序。这种方式更贴合插入排序的本质:把当前元素从链表中取出,插入到它应该在的位置:
list<Song> newSongList = songs_; for (auto itr = next(newSongList.begin()); itr != newSongList.end();) { auto current = itr; ++itr; // 先提前移动迭代器,因为后面会移除current节点 // 找到current应该插入的位置 auto insertPos = newSongList.begin(); while (insertPos != current && Song::CompareTitle(*insertPos, *current)) { ++insertPos; } // 如果不在正确位置,就移动节点过去 if (insertPos != current) { newSongList.splice(insertPos, newSongList, current); } }
这种方法不需要拷贝Song对象,效率更高,也完全避开了赋值操作的问题。
最后提醒一句:你原来的排序逻辑里用*itr和*jtr比较是不符合插入排序逻辑的,插入排序的核心是把当前元素(jtr指向的元素)和它前面的元素依次比较,直到找到合适的位置,这点要注意哦。
内容的提问来源于stack exchange,提问作者Potion

