[英]Modify the value of a std::unordered_map element in C++
我有以下问题。 我有一个std::unordered_map
,其中包含一个对象作为值。 现在,我想修改先前插入的对象。
class Point
{
public:
Point(float _x, float _y) : x(_x), y(_y) {}
float x;
float y;
};
std::unordered_map<int, Point> points;
// ... add some values to it ...
points[1].x = 20.f; // error?
我收到一个奇怪的长期编译错误,提示无法默认构造点。 我对它的理解方式operator []
返回对映射类型(也就是值)的引用,那么为什么不能修改它呢?
如果密钥不在地图中,则需要 operator []
创建密钥。 表达方式
points[1]
需要能够在查找失败的情况下默认插入Point
(无论是否发生查找失败-这是编译时的要求,而不是运行时检查)。 Point
无法满足该要求,因为Point
无法默认构造。 因此,编译错误。 如果要使用unordered_map::operator[]
,则需要添加一个默认构造函数。
如果默认构造的Point
对您的用法没有意义-那么您根本就无法使用operator[]
,而必须在整个过程中使用find
at()
如果可以,请使用at()
除非有例外):
auto it = points.find(1);
if (it != points.end()) {
it->second.x = 20.f;
}
points.at(1).x = 20.f; // can throw
如果给定键不存在任何元素, operator[]
将就地构造一个映射类型的对象。 在具有默认分配器的映射中, operator[]
要求映射类型是默认可构造的 。 更一般而言,映射类型必须是可置换的 。
简单的解决方案是向您的类添加默认构造函数。
Point() : Point(0.f, 0.f) {}
如果无法做到这一点,则必须使用其他功能来访问地图元素。
要访问现有的映射对象,可以使用at
,如果给定键不存在任何元素,则将抛出std::out_of_range
异常。
points.at(1).x = 20.f;
另外,您可以使用find
,它使用给定的键将迭代器返回到元素,或者如果不存在映射元素,则返回迭代器到映射中最后一个元素之后的元素(请参见end
)。
auto it = points.find(1);
if (it != points.end())
{
it->second = 20.f;
}
如果数据不可默认构造,则无法在map
或unordered_map
上使用operator[]
。 这是因为如果找不到该对象,它将通过default-construction创建它。
简单的解决方案是使您的类型默认可构造。
如果不:
template<class M, class K, class F>
bool access_element( M& m, K const& k, F&& f ) {
auto it = m.find(k);
if (it == m.end())
return false;
std::forward<F>(f)(it->second);
return true;
}
然后:
std::unordered_map<int, Point> points;
points.emplace(1, Point(10.f, 10.f));
access_element(points, 1, [](Point& elem){
elem.x = 20.f;
});
将做什么points[1].x = 20.f;
这样做无需冒任何例外代码的风险,也不必使Point
默认可构造。
这种模式-我们在其中传递了一个函数来将元素变异/访问到容器中-从Haskell monad设计中窃取了一个页面。 我会让它返回optional<X>
而不是bool
,其中X
是传入函数的返回类型,但这有点过头了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.