简体   繁体   English

在C ++中修改std :: unordered_map元素的值

[英]Modify the value of a std::unordered_map element in C++

I have the following problem. 我有以下问题。 I have a std::unordered_map that contains an object as the value. 我有一个std::unordered_map ,其中包含一个对象作为值。 Now I want to modify an object that I previously inserted. 现在,我想修改先前插入的对象。

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?

I get a weird long compile error about point not being able to be default constructed. 我收到一个奇怪的长期编译错误,提示无法默认构造点。 The way I understand it operator [] returns a reference to the mapped type (aka the value), so why can't I modify it? 我对它的理解方式operator []返回对映射类型(也就是值)的引用,那么为什么不能修改它呢?

If the key isn't in the map, operator [] is required to create one. 如果密钥不在地图中,则需要 operator []创建密钥。 The expression 表达方式

points[1]

needs to be able to default-insert a Point in case of lookup failure (regardless of whether lookup failure ever occurs - this is a compile-time requirement not a run-time check). 需要能够在查找失败的情况下默认插入Point (无论是否发生查找失败-这是编译时的要求,而不是运行时检查)。 That requirement cannot be satisfied by Point because Point is not default constructible. Point无法满足该要求,因为Point无法默认构造。 Hence the compile error. 因此,编译错误。 If you want to use unordered_map::operator[] , you'll need to add a default constructor. 如果要使用unordered_map::operator[] ,则需要添加一个默认构造函数。

If a default constructed Point doesn't make sense for your usage - then you simply cannot use operator[] and will have to use find throughout (or at() if you're okay with exceptions): 如果默认构造的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[] constructs an object of mapped type in-place if no element exists with the given key. 如果给定键不存在任何元素, operator[]将就地构造一个映射类型的对象。 In a map with a default allocator , operator[] requires the mapped type to be default constructible . 在具有默认分配器的映射中, operator[]要求映射类型是默认可构造的 More generally, the mapped type must be emplace constuctible . 更一般而言,映射类型必须是可置换的

The easy solution is to add a default constructor to your class. 简单的解决方案是向您的类添加默认构造函数。

Point() : Point(0.f, 0.f) {}

If this isn't possible, you will have to use other functions to access map elements. 如果无法做到这一点,则必须使用其他功能来访问地图元素。

To access an existing mapped object, you can using at , which will throw a std::out_of_range exception if no element exists with the given key. 要访问现有的映射对象,可以使用at ,如果给定键不存在任何元素,则将抛出std::out_of_range异常。

points.at(1).x = 20.f;

Alternatively, you can use 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 ) if no such element exists. 另外,您可以使用find ,它使用给定的键将迭代器返回到元素,或者如果不存在映射元素,则返回迭代器到映射中最后一个元素之后的元素(请参见end )。

auto it = points.find(1);
if (it != points.end())
{
    it->second = 20.f;
}

operator[] cannot be used on a map or unordered_map without the data being default-constructible. 如果数据不可默认构造,则无法在mapunordered_map上使用operator[] This is because if the object is not found, it will create it via default-construction. 这是因为如果找不到该对象,它将通过default-construction创建它。

The easy solution is to make your type default-constructible. 简单的解决方案是使您的类型默认可构造。

if not: 如果不:

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;
}

then: 然后:

std::unordered_map<int, Point> points;
points.emplace(1, Point(10.f, 10.f));
access_element(points, 1, [](Point& elem){
  elem.x = 20.f;
});

will do what points[1].x = 20.f; 将做什么points[1].x = 20.f; does without risking exception code or having to make Point default-constructible. 这样做无需冒任何例外代码的风险,也不必使Point默认可构造。

This pattern -- where we pass a function to mutate/access an element to a container -- is stealing a page from Haskell monad design. 这种模式-我们在其中传递了一个函数来将元素变异/访问到容器中-从Haskell monad设计中窃取了一个页面。 I would make it return optional<X> instead of bool , where X is the return type of the passed in function, but that is going a bit far. 我会让它返回optional<X>而不是bool ,其中X是传入函数的返回类型,但这有点过头了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM