临时容器对象上的迭代器

Iterators on temporary container object

假设我有一个函数returns一个STL容器的值,比如说std::list

std::list<Foo> get_Foolist()
{
    std::list<Foo> lst;
    //populate lst
    return lst;
}

class SomeClass
{
    std::list<Foo> get_Foolist()
    {
        return m_foolst;
    }
    ...
    private:
    std::list<Foo> m_foolst;
};

现在,我有一段代码使用此函数获取列表并按以下方式对其进行迭代,

std::list<Foo>::iterator i = get_Foolist().begin();
//use i like ... cout << *i << endl;

当我看到这段代码时,我觉得它不应该工作,因为我们在一个临时对象上使用迭代器,该对象将在执行表达式后被删除。但是,令我惊讶的是它起作用了。

这适用于 Microsoft Visual Studio 2008 的 STLPort5.2。

后来,当我们删除 STLPort 并开始使用编译器提供的 STL 实现时,我们开始面临崩溃。

据透露,上面的代码不适用于 VS 2008 列表实现,但它适用于 STLPort。

我尝试 运行 它在各种其他编译器上,结果如下,

(GCC 和 Clang 从 http://melpon.org/wandbox/ 开始使用)

现在我的问题是,

  1. 哪个实现是正确的(按照标准)?
  2. 为什么它在 VS 以外的所有实现上都成功?
  1. Which implementation is correct(as per standard)?

想必都是。

  1. Why it succeeds on all implementations other that VS?

这是未定义的行为,因为您正在取消引用无效的迭代器。所以它可能会以无数种方式失败。或者看起来有效,这只是另一种失败模式。

在 C++ 中有许多哲学设计规则(其中一些规则是相反的)但经常应用的规则是 "trust the programmer"。

这条规则允许实现者简单地忽略程序员方面的错误:当你编写的代码做错了什么(比如删除一个对象两次,或者迭代一个不再存在的容器)时,编译器是可以自由地忽略会发生的事情,而不是引发运行时错误。这就是所谓的"undefined behavior".

基本原理是你永远不会这样做,而且在运行时浪费一纳秒来检查这些条件是否发生是不值得的。

如果您因为 OS 阻止了您分配的虚拟地址 space 之外的访问而立即崩溃,您可以认为自己非常幸运。

如果你得到一个显示疯狂结果的随机值,你仍然可以认为自己很幸运......不幸的是,有时你会得到一个 "reasonable" 结果,这将隐藏错误很长时间,因为错误的代码会执行 "correct thing",因此程序员将继续实现其他功能并在错误的代码之上添加更多代码。

换句话说"undefined behavior"意味着任何事情都可能发生,包括什么都没有。

通常情况下,代码只会在损坏程度很高时才会失败(即,当代码正在生产中,而您正急于应对愤怒的客户时)。