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

如何从std::set获取可变元素且不破坏其排序规则?

问题描述

我定义了包含实际数据与ID的结构体Data:

struct Data {
    int value;
    int DataId;
};

我需要频繁根据ID从Data集合中获取元素并修改其value(但不修改ID),因此选择std::set以实现高效排序,并使用如下透明比较器:

class Comparator {
    /// dummy type for declaring is_transparent type
    struct SIsTransparentTag;

   public:
    // 标记透明比较器,允许std::set::find直接用int作为key查找
    using is_transparent = SIsTransparentTag;

    // 两个Data对象间的比较
    bool operator()(const Data &lhs, const Data &rhs) const {
        return lhs.DataId < rhs.DataId;
    }
    // Data对象与ID的比较
    bool operator()(const Data &lhs, const int id) const {
        return lhs.DataId < id;
    }
    // ID与Data对象的比较
    bool operator()(const int id, const Data &rhs) const {
        return id < rhs.DataId;
    }
};

随后编写的读写获取函数存在未定义行为:

Data* GetRWDataFromID(std::set<Data,Comparator> &collection,int id)
{
    auto it = collection.find(id);
    if (it == std::end(collection))
    {
        return nullptr;
    }
    // 此处const_cast去除const属性后修改对象会触发未定义行为
    return const_cast<Data *>(&(*it));
}

核心矛盾:std::set的迭代器指向的是不可变元素,但我仅需修改不影响集合排序的value成员。

需求:

  • 保持对数时间的高效查找
  • ID与数据绑定(获取数据时可直接读取ID,无需额外记录,避免使用std::map)
  • DataId在Data对象创建后不可变(可设为私有并提供只读访问器)
解决方案

方法1:将value声明为mutable

这是最直接的方案:把Data中的value标记为mutable,允许在const上下文中修改它;同时把DataId设为const私有成员,通过构造函数初始化,保证创建后不可修改。

修改后的Data结构体

struct Data {
private:
    const int DataId; // const保证创建后不可修改
public:
    mutable int value; // mutable允许const迭代器修改该成员

    // 构造函数初始化ID和value,确保ID只能在创建时设置
    Data(int id, int val) : DataId(id), value(val) {}

    // 只读访问器,对外暴露ID
    int getId() const { return DataId; }
};

调整后的比较器

class Comparator {
    struct SIsTransparentTag;
public:
    using is_transparent = SIsTransparentTag;

    bool operator()(const Data &lhs, const Data &rhs) const {
        return lhs.getId() < rhs.getId();
    }
    bool operator()(const Data &lhs, const int id) const {
        return lhs.getId() < id;
    }
    bool operator()(const int id, const Data &rhs) const {
        return id < rhs.getId();
    }
};

安全的获取函数

此时无需const_cast,直接通过const迭代器修改mutable成员即可,完全符合标准:

// 或者直接在函数内完成修改,避免返回指针
void UpdateDataValue(std::set<Data,Comparator> &collection,int id, int new_val)
{
    auto it = collection.find(id);
    if (it != std::end(collection))
    {
        it->value = new_val; // 合法修改mutable成员
    }
}

// 如果需要返回可操作的指针
Data* GetRWDataFromID(std::set<Data,Comparator> &collection,int id)
{
    auto it = collection.find(id);
    if (it == std::end(collection))
    {
        return nullptr;
    }
    // 此处const_cast仅用于暴露指针,修改value是合法的
    return const_cast<Data*>(&(*it));
}

方法2:用std::set存储std::unique_ptr<Data>

如果不想使用mutable,可以让std::set存储智能指针。std::set的元素是不可变的指针,但指针指向的Data对象可以修改(只要保证DataId不变)。

修改后的Data结构体

struct Data {
private:
    const int DataId;
public:
    int value;

    Data(int id, int val) : DataId(id), value(val) {}
    int getId() const { return DataId; }
};

调整后的比较器

比较器需要基于指针指向的Data对象的ID进行排序:

class Comparator {
    struct SIsTransparentTag;
public:
    using is_transparent = SIsTransparentTag;

    bool operator()(const std::unique_ptr<Data>& lhs, const std::unique_ptr<Data>& rhs) const {
        return lhs->getId() < rhs->getId();
    }
    bool operator()(const std::unique_ptr<Data>& lhs, const int id) const {
        return lhs->getId() < id;
    }
    bool operator()(const int id, const std::unique_ptr<Data>& rhs) const {
        return id < rhs->getId();
    }
};

获取函数

直接返回指针指向的Data对象,无需任何类型转换:

Data* GetRWDataFromID(std::set<std::unique_ptr<Data>, Comparator>& collection, int id)
{
    auto it = collection.find(id);
    if (it == collection.end()) {
        return nullptr;
    }
    return it->get(); // 返回可修改的Data指针
}

这种方案的优势是Data结构不需要特殊标记,内存管理由std::unique_ptr自动处理。

内容的提问来源于stack exchange,提问作者Oersted

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 17:10:56