临时容器对象上的迭代器
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。
我尝试 运行 它在各种其他编译器上,结果如下,
- 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/ 开始使用)
现在我的问题是,
- 哪个实现是正确的(按照标准)?
- 为什么它在 VS 以外的所有实现上都成功?
- Which implementation is correct(as per standard)?
想必都是。
- Why it succeeds on all implementations other that VS?
这是未定义的行为,因为您正在取消引用无效的迭代器。所以它可能会以无数种方式失败。或者看起来有效,这只是另一种失败模式。
在 C++ 中有许多哲学设计规则(其中一些规则是相反的)但经常应用的规则是 "trust the programmer"。
这条规则允许实现者简单地忽略程序员方面的错误:当你编写的代码做错了什么(比如删除一个对象两次,或者迭代一个不再存在的容器)时,编译器是可以自由地忽略会发生的事情,而不是引发运行时错误。这就是所谓的"undefined behavior".
基本原理是你永远不会这样做,而且在运行时浪费一纳秒来检查这些条件是否发生是不值得的。
如果您因为 OS 阻止了您分配的虚拟地址 space 之外的访问而立即崩溃,您可以认为自己非常幸运。
如果你得到一个显示疯狂结果的随机值,你仍然可以认为自己很幸运......不幸的是,有时你会得到一个 "reasonable" 结果,这将隐藏错误很长时间,因为错误的代码会执行 "correct thing",因此程序员将继续实现其他功能并在错误的代码之上添加更多代码。
换句话说"undefined behavior"意味着任何事情都可能发生,包括什么都没有。
通常情况下,代码只会在损坏程度很高时才会失败(即,当代码正在生产中,而您正急于应对愤怒的客户时)。
假设我有一个函数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。
我尝试 运行 它在各种其他编译器上,结果如下,
- 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/ 开始使用)
现在我的问题是,
- 哪个实现是正确的(按照标准)?
- 为什么它在 VS 以外的所有实现上都成功?
- Which implementation is correct(as per standard)?
想必都是。
- Why it succeeds on all implementations other that VS?
这是未定义的行为,因为您正在取消引用无效的迭代器。所以它可能会以无数种方式失败。或者看起来有效,这只是另一种失败模式。
在 C++ 中有许多哲学设计规则(其中一些规则是相反的)但经常应用的规则是 "trust the programmer"。
这条规则允许实现者简单地忽略程序员方面的错误:当你编写的代码做错了什么(比如删除一个对象两次,或者迭代一个不再存在的容器)时,编译器是可以自由地忽略会发生的事情,而不是引发运行时错误。这就是所谓的"undefined behavior".
基本原理是你永远不会这样做,而且在运行时浪费一纳秒来检查这些条件是否发生是不值得的。
如果您因为 OS 阻止了您分配的虚拟地址 space 之外的访问而立即崩溃,您可以认为自己非常幸运。
如果你得到一个显示疯狂结果的随机值,你仍然可以认为自己很幸运......不幸的是,有时你会得到一个 "reasonable" 结果,这将隐藏错误很长时间,因为错误的代码会执行 "correct thing",因此程序员将继续实现其他功能并在错误的代码之上添加更多代码。
换句话说"undefined behavior"意味着任何事情都可能发生,包括什么都没有。
通常情况下,代码只会在损坏程度很高时才会失败(即,当代码正在生产中,而您正急于应对愤怒的客户时)。