提问人:Foaly 提问时间:3/17/2016 最后编辑:BarryFoaly 更新时间:3/18/2016 访问量:6778
修改 C++ 中 std::unordered_map 元素的值
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]
在查找失败的情况下,需要能够默认插入 a(无论是否发生过查找失败 - 这是编译时要求,而不是运行时检查)。该要求不能满足,因为默认情况下不可构造。因此出现编译错误。如果要使用 ,则需要添加默认构造函数。Point
Point
Point
unordered_map::operator[]
如果默认构造对您的使用没有意义 - 那么您根本无法使用,并且必须始终使用(或者如果您可以接受例外情况):Point
operator[]
find
at()
auto it = points.find(1);
if (it != points.end()) {
it->second.x = 20.f;
}
points.at(1).x = 20.f; // can throw
评论
emplace
std::make_pair
points.at(1)
points[1]
points[1].x
points.insert
[]
[]
points[1].x
operator[]
如果给定键不存在任何元素,则就地构造映射类型的对象。在具有默认分配器的映射中,要求映射类型为默认可构造类型。更一般地说,映射类型必须是可构造的。operator[]
简单的解决方案是将默认构造函数添加到您的类中。
Point() : Point(0.f, 0.f) {}
如果无法做到这一点,则必须使用其他函数来访问地图元素。
要访问现有的映射对象,可以使用 at
,如果给定键不存在元素,它将引发 std::out_of_range
异常。
points.at(1).x = 20.f;
或者,您可以使用 find
,它将迭代器返回到具有给定键的元素,或者返回映射中最后一个元素后面的元素(见结尾
),如果不存在此类元素。
auto it = points.find(1);
if (it != points.end())
{
it->second = 20.f;
}
operator[]
不能在默认可构造的数据上使用,或者没有默认可构造的数据。这是因为如果找不到该对象,它将通过默认构造创建它。map
unordered_map
简单的解决方案是使您的类型默认可构造。
如果没有:
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
评论
Point
Point() : x(0), y(0) {}