如何在C++中的Map或Set中修改Key值


我們對map中的修改value操作可以是如下的:

auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
myMap.find("two")->second = 22;

但是如果想修改key操作,則下面這段代碼是錯誤的:

auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
myMap.find("two")->first = "dos";

如果是map<int, int>,則會報這樣的錯誤:

error: assignment of read-only member ‘std::pair<const int, int>::first’
m.find(1)->first=3;

修改std::map的key值的問題

std::vectorstd::mapstd::set這些序列容器中,需要提供兩個保證:

  • 元素應該以有序的順序來存儲
  • 確保元素是唯一的

但是在map的情況下,排序順序很方便,以便找到與對數復雜度的鍵關聯的值。為了保持這些不變性,容器std::map和std::set需要對它們的值在集合內的相對位置進行一些控制。

如果只是使用迭代器來修改值,例如之前使用的示例,則不會通知該容器。 這將使其結構不一致並破壞不變性。所以是read-only member

C++17中的key值修改

修改key的策略是通過容器攜帶的接口,而不是通過迭代器進行修改

c++17中給關聯容器提供了一個方法:extract,這提供了保存容器元素的節點:

auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
 
auto const node = myMap.extract("two");

extract對容器具有修改作用:映射不再包含節點。 如果我們在調用提取之前和之后檢查大小:

auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
 
std::cout << myMap.size() << '\n'; // 3
auto node = myMap.extract("two");
std::cout << myMap.size() << '\n'; // 2

所以此時node並不是和map中的元素相關聯,所以不會破壞map的結構,修改key值就可以:

node.key() = "dos";

而這個node並沒有提供value的修改方法,因為在map中直接操作會更加方便。

修改key的值后,需要將node重新插回到map結構中:

myMap.insert(std::move(node));

這里必須寫上std::move,因為node只有移動構造,並沒有拷貝構造函數。

當請求的node並不存在的時候

auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
 
auto node = myMap.extract("four");

node仍然是個有效的對象,我們可以將其用insert插入,但是不能訪問它的key();所以最有效的措施應該是在extract之前判斷一下是否為空:

auto myMap = std::map<std::string, int>{ {"one", 1}, {"two", 2}, {"three", 3} };
 
auto node = myMap.extract("two");
if (!node.empty())
{
    node.key() = "dos";
    myMap.insert(std::move(node));
}

C++17之前怎么做

在C++17之前,做法就是erase key所在的元素然后再更新后插入,但是這樣的做法非常的低效而且不直接。

封裝node

修改一個map的key值,比提取一個node、修改相應的值、最后插回map中這種表達會更清晰,雖然后者是前者的實現思想。所以我們可以將后者進行封裝,抽象為前者的表達:

template<typename Container>
void replaceKey(Container& container,
                const typename Container::key_type& oldKey,
                const typename Container::key_type& newKey)
{
    auto node = container.extract(oldKey);
    if(!node.empty())
    {
        node.key() = newKey;
        container.insert(std::move(node));
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM