簡體   English   中英

是否可以從std :: set刪除修改后的元素?

[英]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段。

除非您的modify()執行使迭代器無效的任何操作,否則它是安全 如果它無法訪問s變量,則沒有問題。

您的代碼是正確的。 該集合包含四個指針 它不包含那些指針指向的內容。

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'

*ite是兩個不同的對象。

通常,由STL集/映射迭代器指向的對象和用作搜索關鍵字的對象是不同的對象。 因此,您仍將在處理容器對象的副本

現在,您的其他問題可以歸結為兩個:

  • 我可以修改itr指向的對象嗎? 會影響排序順序嗎?

    • 從理論上講是可能的。 集的內容為const,並且迭代器不允許直接分配-

       test.cpp10:9: error: read-only variable is not assignable. *it = 4; ~~~ ^ 

      -但是使用mutableconst_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM