在 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 []
returns 对映射类型(又名值)的引用,为什么我不能修改它?
如果密钥不在地图中,operator []
需要 创建一个。表达式
points[1]
需要能够在查找失败的情况下默认插入 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
operator[]
constructs an object of mapped type in-place if no element exists with the given key. In a map with a default allocator, operator[]
requires the mapped type to be default constructible. More generally, the mapped type must be emplace constuctible.
简单的解决方案是将默认构造函数添加到您的 class。
Point() : Point(0.f, 0.f) {}
如果这不可能,您将不得不使用其他函数来访问地图元素。
要访问现有映射对象,如果不存在具有给定键的元素,您可以使用 at
, which will throw a std::out_of_range
异常。
points.at(1).x = 20.f;
或者,如果不存在这样的元素,您可以使用 find
, which returns an iterator to the element with the given key, or to the element following the last element in the map (see end
)。
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
默认可构造。
这种模式——我们将函数传递给 mutate/access 将元素传递给容器——正在从 Haskell monad 设计中窃取页面。我会将其设为 return optional<X>
而不是 bool
,其中 X
是传入函数的 return 类型,但这有点过分了。
我有以下问题。我有一个 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 []
returns 对映射类型(又名值)的引用,为什么我不能修改它?
如果密钥不在地图中,operator []
需要 创建一个。表达式
points[1]
需要能够在查找失败的情况下默认插入 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
operator[]
constructs an object of mapped type in-place if no element exists with the given key. In a map with a default allocator, operator[]
requires the mapped type to be default constructible. More generally, the mapped type must be emplace constuctible.
简单的解决方案是将默认构造函数添加到您的 class。
Point() : Point(0.f, 0.f) {}
如果这不可能,您将不得不使用其他函数来访问地图元素。
要访问现有映射对象,如果不存在具有给定键的元素,您可以使用 at
, which will throw a std::out_of_range
异常。
points.at(1).x = 20.f;
或者,如果不存在这样的元素,您可以使用 find
, which returns an iterator to the element with the given key, or to the element following the last element in the map (see end
)。
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
默认可构造。
这种模式——我们将函数传递给 mutate/access 将元素传递给容器——正在从 Haskell monad 设计中窃取页面。我会将其设为 return optional<X>
而不是 bool
,其中 X
是传入函数的 return 类型,但这有点过分了。