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