龙空技术网

C++ 高效地修改映射项的键

启辰8 45

前言:

今天我们对“我的世界映射键位设置在哪”大体比较关心,我们都需要学习一些“我的世界映射键位设置在哪”的相关内容。那么小编也在网络上搜集了一些关于“我的世界映射键位设置在哪””的相关内容,希望小伙伴们能喜欢,看官们一起来了解一下吧!

高效地修改映射项的键

映射是一个关联容器,它存储键值对。容器按键排序。键必须是唯一的,并且它们是 const-qualified,因此它们不能被更改。

例如,如果我填充一个映射并尝试更改键,我会在编译时得到一个错误:

map<int, string> mymap {    {1, "foo"}, {2, "bar"}, {3, "baz"}};auto it = mymap.begin();it->first = 47;

输出:

error: assignment of read-only member ...    5 |      it->first = 47;       |      ~~~~~~~~~~^~~~

如果你需要重新排序映射容器,你可以通过使用 extract() 方法交换键来实现。

C++17 新增的 extract() 是映射类及其衍生类的成员函数。它允许从序列中提取映射的元素,而不影响有效载荷。一旦提取,键不再 const-qualified,可以被修改。

让我们看一个例子。

如何做到这一点…

在这个例子中,我们将定义一个映射,代表比赛中的参赛者。在比赛的某个时刻,顺序发生了变化,我们需要修改映射的键。

我们首先定义映射类型的别名:

using Racermap = map<unsigned int, string>;

这允许我们在代码中一致地使用类型。

我们将为打印映射编写一个函数:

void printm(const Racermap &m) {    cout << "Rank:\n";    for (const auto& [rank, racer] : m) {        cout << format("{}:{}\n", rank, racer);    }}

我们可以在任何时候将映射传递给这个函数来打印出当前的排名。

在我们的 main() 函数中,我们定义了一个具有参赛者初始状态的映射:

int main() {    Racermap racers {        {1, "Mario"}, {2, "Luigi"}, {3, "Bowser"},        {4, "Peach"}, {5, "Donkey Kong Jr"}    };    printm(racers);    node_swap(racers, 3, 5);    printm(racers);}

键是一个表示参赛者排名的 int。值是一个包含参赛者名称的字符串。

然后我们调用 printm() 打印当前排名。对 node_swap() 的调用将交换两个参赛者的键,然后我们再次打印。

在某个时刻,一个参赛者落后了,另一个参赛者抓住机会在排名中上升。node_swap() 函数将交换两个参赛者的排名:

template<typename M, typename K>bool node_swap(M & m, K k1, K k2) {    auto node1{ m.extract(k1) };    auto node2{ m.extract(k2) };    if(node1.empty() || node2.empty()) {        return false;    }    swap(node1.key(), node2.key());    m.insert(move(node1));    m.insert(move(node2));    return true;}

这个函数使用映射的 extract() 方法提取指定的元素。这些提取的元素称为节点。

节点是 C++17 开始的新概念。这允许从映射类型结构中提取元素,而不影响元素本身。节点被取消链接,返回节点句柄。一旦提取,节点句柄通过节点的 key() 函数提供对键的可写访问。然后我们可以交换键并将它们重新插入映射中,而无需复制或操作有效载荷。

当我们运行这段代码时,我们得到映射的打印输出,节点交换前后:

Rank:1:Mario2:Luigi3:Bowser4:Peach5:Donkey Kong JrRank:1:Mario2:Luigi3:Donkey Kong Jr4:Peach5:Bowser

这一切都是通过 extract() 方法和新的 node_handle 类实现的。让我们更仔细地看看这是如何工作的。

它是如何工作的…

这种技术使用了新的 extract() 函数,它返回一个 node_handle 对象。顾名思义,node_handle 是对节点的句柄,它由一个关联元素及其相关结构组成。extract 函数在保留节点位置的同时与其解关联,并返回一个 node_handle 对象。这有效地从关联容器中移除了节点,而没有触及数据本身。node_handle 允许你访问解关联的节点。

node_handle 有一个成员函数 key(),它返回对节点键的可写引用。这允许你在节点从容器中解关联时更改键。

还有更多…

使用 extract() 和 node_handle 时需要注意以下几点:

如果找不到键,extract() 函数将返回一个空的节点句柄。你可以使用 empty() 函数测试节点句柄是否为空:

auto node{ mapthing.extract(key) };if(node.empty()) {    // node handle is empty}

extract() 函数有两个重载版本:

node_type extract(const key_type& x);node_type extract(const_iterator position);

我们使用了第一种形式,通过传递一个键。你也可以使用迭代器,这通常不需要查找。

请记住,你不能从字面量中获取引用,所以像 extract(1) 这样的调用通常会因为段错误而崩溃。

插入映射时键必须保持唯一。

例如,如果我尝试将一个键更改为映射中已经存在的值:

auto node_x{ racers.extract(racers.begin()) };node_x.key() = 5;  // 5 is Donkey Kong Jrauto status = racers.insert(move(node_x));if(!status.inserted) {    cout << format("insert failed, dup key: {}", status.position->second);    exit(1);}

插入失败,我们得到了错误消息:

insert failed, dup key: Donkey Kong Jr

在这个例子中,我将 begin() 迭代器传递给 extract()。然后我将键分配了一个已经在使用中的值(5, Donkey Kong Jr)。插入失败,结果的状态 status.inserted 是 false。status.position 是找到的键的迭代器。在 if() 块中,我使用 format() 打印找到的键的值。

标签: #我的世界映射键位设置在哪