[英]Is it possible to erase modified element from std::set?
我需要一個可以修改元素的排序集合。 修改后擦除元素是否安全? 排序鍵可以修改。
auto it=s.find(e)
modify(e)
s.erase(it)
我已經在VS2010中進行了一些測試,並且工作正常。 我認為delete(it)不需要搜索元素,因此不需要在要擦除的元素上調用compare。
很難修改整個程序以在修改之前刪除元素,這就是為什么我正在尋找替代解決方案的原因。
編輯:添加工作樣本以使其更加清晰
#include <iostream>
#include <algorithm>
#include <set>
template <typename T>
struct PtrCmp
{
bool operator()(const T* x, const T* y) const
{
return *x<*y;
}
};
int main()
{
std::set<int*, PtrCmp<int>> aset;
int t[]={1,2,3,4};
for(int i=0;i<4;++i)
aset.insert(&t[i]);
auto it=aset.find(&t[2]);
t[2]=5;
aset.erase(it);
for(auto it=aset.begin(); it!=aset.end(); ++it)
std::cout<<**it<<std::endl;
}
在您的示例中, t[2]=5;
修改比較器中使用的值,該比較器用於例如重新平衡set
容器后的樹數據結構。 因此,這種修改是不安全的,因為如果更改了節點上的實際密鑰,則平衡樹算法可能會失敗,因此與該樹節點的期望值不匹配。 erase
操作很可能觸發樹的重新平衡,這就是您獲得未定義行為的方式。 因此,通過修改比較器中使用的值,實際上會破壞set
容器后面的樹的平衡性。
modify(e)
(也許)不修改集合。 我的猜測是e
是集之外的東西,那s.find(e)
的搜索設定為(副本)內e
。 基本上,您的代碼等效於:
auto it=s.find(e)
s.erase(it)
modify(e)
這樣就不會有任何問題。
除非e
實際上是指向數據的原始指針。
std::set<T>
中的元素被認為是const
。 如果更改元素(例如,通過具有mutable
成員或指向某些內部數據的指針並更改鍵),則會違反std::set<T>
的不變量,並且在訪問std::set<T>
時會出現未定義的行為。以任何方式std::set<T>
。
集合中的元素與e
無關(或不應該)。 如果您修改e
並且它不影響set元素,那么您就可以了。
如果修改確實以修改其排序順序的方式影響set元素,則此刻您在技術上已獲得未定義的行為,並且以后不再需要erase
。
參見C ++ 11 23.2.4第4和5段。
您的代碼是正確的。 該集合包含四個指針 。 它不包含那些指針指向的內容。
t[2] = 5;
不會以任何方式修改set
。
口頭表達:
// I have this key 'e'...
auto it=s.find(e) // I look through my set for another like 'e'
// and 'it' now points to it
modify(e) // now I 'invert' e.
s.erase(it) // and I remove object pointed to by 'it'
*it
和e
是兩個不同的對象。
通常,由STL集/映射迭代器指向的對象和用作搜索關鍵字的對象是不同的對象。 因此,您仍將在處理容器對象的副本 。
現在,您的其他問題可以歸結為兩個:
我可以修改itr指向的對象嗎? 會影響排序順序嗎?
從理論上講是可能的。 集的內容為const,並且迭代器不允許直接分配-
test.cpp10:9: error: read-only variable is not assignable. *it = 4; ~~~ ^
-但是使用mutable
或const_cast
和指針魔術的組合,可以修改const-data。 危險嗎? 你打賭 。 每個容器都有其自己的假設,即其API保持不變。 因此,一般規則-絕不對API不允許的STL容器進行填充。
我可以得到一個可以修改其元素的分類容器嗎?
當然! 您只需在容器外將其放回去即可。
auto it=s.find(e); // possible optimisation (see below) s.erase(it); modify(e); auto position = s.insert(e); if (position.first) // no existing values matching e's comparison! assert(*position.second.key == e.key);
使用C ++ 11,根據'e'的含義,您可以使用移動語義將非比較元素從* it移到e,並與set emplace一起使用,以優化完成的副本數。
在示例中,您修改了set
的順序,因此具有未定義的行為(“按需工作”是一種可能的行為:-/)。
您可以使用以下代碼獲得正確的代碼:
auto it = aset.find(&t[2]);
assert(it != aset.end()); // or any other error check methods
aset.erase(it);
t[2] = 5; // No longer used by set
要么
auto it = aset.find(&t[2]);
assert(it != aset.end()); // or any other error check methods
auto* ref = *it;
aset.erase(it);
assert(ref != nullptr); // or any other error check methods
*ref = 5; // No longer used by set.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.