临时容器对象上的迭代程序

Iterators on temporary container object

本文关键字:迭代 程序 对象      更新时间:2023-10-16

假设我有一个函数,它按值返回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;

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

这与STLPort5.2和Microsoft Visual Studio 2008配合使用。

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

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

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

  • Visual Studio 2008(不带STLport5.2)-崩溃
  • Visual Studio 2008(带STLPort5.2)-工作
  • Visual Studio 2013(不带STLport5.2)-崩溃
  • GCC 4.3.6(不含STLPort5.2)-工程
  • Clang 3.0(不带STLPort5.2)-工程

(GCC和Clang来自http://melpon.org/wandbox/)

现在我的问题是,

  1. 哪个实施是正确的(根据标准)
  2. 为什么它在VS之外的所有实现上都成功了
  1. 哪个实施是正确的(根据标准)

大概都是吧。

  1. 为什么它在VS之外的所有实现上都成功了

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

在C++中有许多哲学设计规则(其中一些规则告诉一个规则与另一个规则相反),但一个经常应用的规则是"信任程序员"。

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

其原理是,你永远不会这样做,在运行时浪费哪怕只是一纳秒来检查这些情况是否发生也不值得。

如果你因为操作系统阻止了你分配的虚拟地址空间之外的访问而立即崩溃,你可以认为自己非常幸运。

如果你得到一个随机值,显示疯狂的结果,你仍然可以认为自己很幸运。。。不幸的是,有时你会得到一个"合理"的结果,这会在很长一段时间内隐藏错误,因为错误的代码会做"正确的事情",因此程序员会继续实现其他功能,并在错误的代码上添加更多的代码。

换句话说,"未定义的行为"意味着任何事情都可能发生,包括什么都不发生。

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