使用const键但非const值映射?

时间:2017-05-09 07:41:02

标签: c++ dictionary const

我有一种情况,我希望有一个地图,在初始化后不允许添加/删除键,但允许更改值(因此我不能简单地制作地图const)。即

/*semi-const*/ map<int,int> myMap = initMap();

myMap[1] = 2;                  // NOT OK, because potentially adds a new key
myMap.at(1) = 2;               // OK, because works only if key is present
for (auto & element : myMap) {
    element.second = 0;        // OK, values may change
}

我可以为std::map编写自己的包装器,但我觉得它不是很常见,所以我想知道是否已有解决方案。

地图是否有一些标准习惯用法不允许添加/删除键,而值可能会更改?

ps:我知道单独的标题有点模糊,因为键在地图中已经是常量,但我希望很清楚我的意思......

5 个答案:

答案 0 :(得分:4)

您是否可以创建一个包含值的包装器,该值允许在const时对值进行变更并将其放入map中?类似的东西:

template<typename T>
class Mutable {
    mutable T value;
public:
  const Mutable& operator=(const T& v) const { value = v; return *this; }
  T& get() const { return value; }  
};

然后您的地图可以是

类型
const std::map<int, Mutable<int>>

Live demo

答案 1 :(得分:2)

我通常认为这是C ++中的一个陷阱而不是一个功能,但是,如果它适合您的应用程序,您可以只使用指针值。

#include <map>
#include <memory>

int main(int argc, char ** argv)
{
    using namespace std;
    const map<int, shared_ptr<int>> myMap = { {1, make_shared<int>(100)} };
    // *(myMap[1]) = 2;  // Does not compile
    *(myMap.at(1)) = 2;
    for (auto & element : myMap)
    {
        *(element.second) = 0;
    }
    return 0;
}

这只是this other answer的更简单版本(显然,您可以根据需要在shared_ptr / unique_ptr之间进行选择。)

答案 2 :(得分:1)

标准库中的容器是针对一种用途进行优化的类,这些用法预期将用作或包含在更高级别的类中。

此处您的要求(初始化后修复的密钥)不包含在标准库容器中,因此您必须构建自己的实现。由于std::map,您只需实施所需的操作,可能只需operator [] ...

答案 3 :(得分:1)

据我所知,您只想禁用索引访问操作符,以便用户不会意外地将默认构造项添加到地图中。我的解决方案受到了Chris Drew的解决方案的启发,但还有一个额外的好处,即保持const正确(即当地图为常量时不允许更改地图的值)。

基本上,通过禁用默认构造,您可以删除调用std::map提供的索引访问运算符的功能。其他方法将保持可用,因为std::map是类模板,并且在调用成员函数之前不会对其进行求值。因此,std::map::at将正常工作,但std::map::operator[]将导致编译时错误。

受Chris的启发,您可以使用mapped_type上的包装器来禁用默认构造。我拿了他的演示并稍微调整了一下,以演示如何禁用默认构造并将其与std::map而不是const std::map一起使用。

template<typename T>
class RemoveDefaultConstruction {
   T value;
public:
   RemoveDefaultConstruction() = delete; // The magic is here
    RemoveDefaultConstruction(const RemoveDefaultConstruction &other) noexcept(std::is_nothrow_copy_constructible<T>::value) = default;
    RemoveDefaultConstruction(RemoveDefaultConstruction &&other) noexcept(std::is_nothrow_move_constructible<T>::value) = default;
    RemoveDefaultConstruction(T &&t) noexcept(std::is_nothrow_constructible<T, decltype(std::forward<T>(t))>::value) :
    value{std::forward<T>(t)} {
    }

    RemoveDefaultConstruction& operator=(const RemoveDefaultConstruction &other) = default;
    RemoveDefaultConstruction& operator=(RemoveDefaultConstruction &&other) = default;
    RemoveDefaultConstruction& operator=(T &&other) { value = std::move(other); return *this; }
    RemoveDefaultConstruction& operator=(T const &other) { value = other; return *this; }

    T const &get() const { return value; } // Keep const correctness
    T &get() { return value; } // Keep const correctness
};

void update(std::map<int, RemoveDefaultConstruction<int>> &m, int k, int v) { m.at(k) = v; }
void update(std::map<int, RemoveDefaultConstruction<int>> const &m, int k, int v) {
   //m.at(k) = v; // ERROR: Cannot change a const value
}

Live Demo

答案 4 :(得分:0)

我在这里看到2个选项

  1. 制作地图const并在更改内容时使用const_cast

    const std :: map myMap;

    myMap [1] = 2; //不行,因为const map

    (const_cast&amp;&gt;(myMap))。at(1)= 2; //使用const_cast

  2. 确定
  3. 制作包装类或派生只读取和更新现有值方法的自定义地图

  4. 我认为没有内置方法只使用更新值制作地图,并限制和插入。