我有一种情况,我希望有一个地图,在初始化后不允许添加/删除键,但允许更改值(因此我不能简单地制作地图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:我知道单独的标题有点模糊,因为键在地图中已经是常量,但我希望很清楚我的意思......答案 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>>
答案 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
}
答案 4 :(得分:0)
我在这里看到2个选项
制作地图const并在更改内容时使用const_cast
const std :: map myMap;
myMap [1] = 2; //不行,因为const map
(const_cast&amp;&gt;(myMap))。at(1)= 2; //使用const_cast
制作包装类或派生只读取和更新现有值方法的自定义地图
我认为没有内置方法只使用更新值制作地图,并限制和插入。