You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中以自定义类为std::map键时如何修改键对象非key属性

问题描述

我有如下类定义:

#include <string>

class Item {
public: 
   Item(std::string name, int id);
   virtual int getWeight() = 0;

protected:
   std::string name;
   const int id;
};
#include <vector>
#include <memory>
#include "Item.h"

class Bucket : Item { 
public:
    Bucket(std::string name, std::string type, int id); // 实现中会调用Item的构造函数
    bool operator<(const Bucket& b) const {
        return (id < b.id );
    }
    void addItem(std::shared_ptr<Item> item) {
      this->weight += item->getWeight();
      this->items.push_back(item);
    }
    int getWeight() override; // 该实现与当前问题无关

private:
    std::string type;
    std::vector<std::shared_ptr<Item>> items;
    int weight = 0;

};

还有其他类继承自Item类,为简化问题此处仅展示Bucket类。

现在我要在main函数中遍历已经存在条目的map,调用方法修改键对象的某个属性,main.cpp代码如下:

#include <map>
#include <memory>
#include <vector>
#include <algorithm>
#include "Item.h"
#include "Bucket.h"

using namespace std;

int main(){
   map<Bucket, vector<shared_ptr<Item>>> map; // 假设该map已有若干条目
   for(auto itr = map.begin(); itr != map.end(); itr++){
        for_each(itr->second.begin(), itr->second.end(), [&itr](shared_ptr<Item> item){
            itr->first.addItem(item); // 此处编译不通过,错误信息见下文
        });
   }
}

如上代码在itr->first.addItem(item);行编译报错:'this' argument to member function 'addItem' has type 'const Bucket', but function is not marked const.

我不能将addItem方法标记为const,因为它会修改对象属性,修改为const也会报错。我理解的问题原因是Bucket作为map的键存入后会变为const类型。请问有没有办法不将map改为map<int, pair<Bucket,vector<shared_ptr<Item>>>>的结构,仅告知编译器只将Bucket的id属性作为map的const键,从而可以修改Bucket的其他属性?还是我对问题根源的理解有误?


解决方案

你对问题根源的理解完全正确:std::map作为有序关联容器,内部依赖键的排序结果维护红黑树结构,因此所有键默认是const只读的,禁止修改避免破坏容器结构。
你的Bucket类的比较运算符仅用到了本身就是const修饰的id字段,修改其他属性不会影响键的排序结果,有两种合法方案可以实现需求:

方案1:用mutable修饰非比较属性

将不参与键比较的可修改成员标记为mutable,这类成员允许在const对象中被修改,同时将addItem方法标记为const即可:

class Bucket : Item { 
public:
    // 其他成员不变
    void addItem(std::shared_ptr<Item> item) const {
      this->weight += item->getWeight();
      this->items.push_back(item);
    }
private:
    std::string type;
    mutable std::vector<std::shared_ptr<Item>> items;
    mutable int weight = 0;
};

该方案改动最小,性能无额外损耗,只要确保参与比较的id永远不被修改即可。

方案2:使用C++17的extract方法操作节点

如果不想修改类的定义,可以用std::map的extract接口取出节点,修改键后再插回容器,因为id不变,插回后不会改变容器结构,开销极低:

int main(){
   map<Bucket, vector<shared_ptr<Item>>> map; 
   for(auto itr = map.begin(); itr != map.end(); ){
        auto node = map.extract(itr++);
        for_each(node.mapped().begin(), node.mapped().end(), [&node](shared_ptr<Item> item){
            node.key().addItem(item);
        });
        map.insert(std::move(node));
   }
}

另外注意你原代码中的for_each第二个参数误写为begin,需修改为end才能正常遍历整个vector。


内容的提问来源于stack exchange,提问作者L.Gashi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 10:06:04