对象能否从标准 C++ 容器中擦除自身?

Can an object erase itself from a standard C++ container?

代码如下

#include <iostream>
#include <map>

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    m.erase(i);
  }
};

int main()
{
  std::map<int, foo> m;

  m.emplace(1, foo() );

  std::cout << m.size() << std::endl;

  m[1].kill(m, 1);

  std::cout << m.size() << std::endl;
}

编译时没有警告 (g++),执行时没有错误,根据输出判断 kill 方法从映射中删除了 foo 对象。但是,我觉得这实际上可能是未定义的行为。似乎在 kill 方法中 m.erase(i) this 行之后不再指向有效对象。

C++ 标准对此有何规定?

当您输入 kill 时,m[1](来自 m[1].kill(m, 1);)语句已被完全评估为您正在调用 killfoo 对象上。

然后你 m.erase(i); 最终销毁当前对象 foo

就您在 kill 函数 return 之前绝对没有使用当前对象 (this) 编写语句而言,这是完全可以接受和安全的(正如Auriga and Barry 引用的帖子)。即使当前对象不再存在,您的函数也会 return 安全地从堆栈中取出,据我所知没有理由失败。

举个例子,这将以未定义的行为结束,绝不能这样做:

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    m.erase(i);
    cout << attribute; // don't do that! current foo object does not exist anymore
  }
  int attribute;
};

那么假设你正在做的事情是有风险的,但如果你做得好,那就是有效和安全的。

作为示例,这将以定义的行为结束并且可以完成:

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    int theAttribute = attribute;
    m.erase(i);
    cout << theAttribute; // OK!
  }
  int attribute;
};

无论如何,让方法删除当前对象可能不是一个好的做法(特别是如果另一个开发人员稍后修改代码......他很容易使上面的第一个示例崩溃)。至少在代码中放一个明确的注释,告诉当前对象可能已经被销毁(注意 kill 可能会销毁当前对象,另一个,或者 none...取决于 m 内容和 i):

struct foo
{
  void kill(std::map<int, foo>& m, int i)
  {
    m.erase(i);
    // careful! current object could have been destroyed by above statement and may not be valid anymore! Don't use it anymore!
  }
};

这根本不安全。如果多次调用 m.erase(i) 将避免尝试擦除同一个对象,但如果多次调用 m[1].kill(m, 1) 是未定义的行为。为了稍微安全一点,m.at(1).kill(m, 1) 会抛出一个out_of_range 错误。