使用临时程序将迭代器获取到STL容器时的细微错误:如何避免

Subtle error when using temporaries to get iterators to a STL container: how to avoid it?

本文关键字:何避免 错误 STL 程序 迭代器 获取      更新时间:2023-10-16

让我们考虑一下这个类:

class X {
    std::map<uint32_t, uint32_t> _map;
public:
    X() { /* Populate the map */ }
    std::map<uint32_t, uint32_t> getTheMap() { return _map; }
};

还有这个错误代码:

X x;
// Statement 1
std::map<uint32_t, uint32_t>::const_iterator it = x.getTheMap().begin();
// Statement 2
std::map<uint32_t, uint32_t>::const_iterator et = x.getTheMap().end();
for (; it != et; it++) {
    /* Access the map using the iterator it */
}

错误的部分是,在Statement 1Statement 2中,我得到了一个临时对象的迭代器,该对象将在每条语句的末尾被销毁。因此,for()循环内部的行为是未定义的。

getTheMap()方法的正确用法是:

std::map<uint32_t, uint32_t> map = x.getTheMap();
std::map<uint32_t, uint32_t>::const_iterator it = map.begin();
std::map<uint32_t, uint32_t>::const_iterator et = map.end();
for (/* [...] */)

必须注意的是,X类存在一些严重的设计问题:

  1. _map应该更好地封装在类中(用于读和写访问),因此可以避免使用getTheMap()方法
  2. 如果确实需要getTheMap()方法,它可以返回对_map的引用

然而,给定类X"原样"(<--请参阅下面的编辑),有没有办法防止用户将迭代器获取到临时迭代器?

EDIT:类X可以更改,但getTheMap方法应该存在并按值返回。然而,我也在考虑编译器警告。

一种可能性是使用这样的包装器:

class X {
  typedef std::map<uint32_t,uint32_t> Map;
  Map _map;
  struct MapWrap {
    const Map &mapref;
    MapWrap(const Map &mapref_arg)
    : mapref(mapref_arg)
    {
    }
    operator Map() const { return mapref; }
  };

public:
  MapWrap getTheMap()
  {
    return MapWrap(_map);
  }
};

这样你就可以得到:

X x;
std::map<uint32_t,uint32_t>::const_iterator iter = x.getTheMap().begin(); // error
std::map<uint32_t,uint32_t> m = x.getTheMap(); // no error

这可以防止像地图一样意外地使用临时的,但会使其成为用户必须使用地图副本的地方。

不在C++03中。在C++11中,标准库应该已经启用了这种保护。

您可以尝试使用std::move强制getTheMap()返回原始对象,但我不确定这在这里是否有效。

如果没有,我想返回成员的唯一/shared_ptr将是最好的选择。