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

时间:2016-03-17 11:32:56

标签: c++ c++11 reference stl unordered-map

我有以下问题。我有一个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 []返回对映射类型(也就是值)的引用,为什么我不能修改它?

3 个答案:

答案 0 :(得分:7)

如果密钥不在地图中,则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

答案 1 :(得分:2)

如果给定键不存在任何元素,则

operator[]就地构造映射类型的对象。在带有default allocator的地图中,operator[]要求映射的类型为default constructible。更一般地,映射类型必须是emplace constuctible

简单的解决方案是为您的类添加默认构造函数。

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

答案 2 :(得分:0)

如果数据不是默认构造的,那么private[streaming] object InternalMapWithStateDStream { private val DEFAULT_CHECKPOINT_DURATION_MULTIPLIER = 10 } operator[]不能使用

map。这是因为如果找不到对象,它将通过default-construction创建它。

简单的解决方案是使您的类型默认可构造。

如果不是:

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;默认可构造。

这个模式 - 我们传递一个函数来改变/访问元素到容器 - 从Haskell monad设计中窃取一个页面。我会让它返回Point而不是optional<X>,其中bool是传入函数的返回类型,但这有点远。