为什么std :: begin和std :: end“不安全存储”?

时间:2019-02-11 07:33:03

标签: c++ c++11 iterator std

Eric Niebler在this blog post中指出:

  

std :: begin和std :: end有什么问题?惊喜!他们不是   记忆安全。考虑一下这段代码的作用:

extern std::vector<int> get_data();
auto it = std::begin(get_data());
int i = *it; // BOOM
     

std :: begin对于const和非const左值有两个重载。麻烦   是,右值绑定到const左值引用,导致悬空   上面的迭代器。

我很难理解他的观点,以及为什么it是悬而未决的参考。 有人可以解释吗?

6 个答案:

答案 0 :(得分:57)

get_data函数返回一个对象。按所示方式使用时,该对象将是 temporary 对象,一旦完整表达式结束,该对象将被销毁。现在,迭代器将引用一个不再存在的矢量对象,并且不能以任何有用的方式对其进行取消引用或使用。

答案 1 :(得分:51)

我认为Eric关于std::begin的观点是,它默默地接受一个右值容器作为自变量。从表面上看,代码问题也以

为例
auto it = get_data().begin();

但是std::begin是一个自由函数模板,可以使它拒绝右值,而无需为每个容器的begin成员添加适当的引用限定符。通过“正当”转发,它错过了为代码添加一层内存安全性的机会。

理想情况下,overload set可能会受益于

template< class C > 
void begin( C&& ) = delete;

那会导致博客文章中的代码被当场彻底拒绝。

答案 2 :(得分:13)

get_data完成后,std::begin返回的临时向量超出范围。它并没有保持活动状态,因此it是迭代器,可以破坏对象。

答案 3 :(得分:4)

可以公平地说这是std :: begin的内存安全故障吗? 没有一种“安全”的方式来创建迭代器,以使删除容器后跟随指针有效。

它没有检查其他方法具有的功能,但这并不是没有办法使派生范围的迭代器流行起来。您只需要再努力一点...

答案 4 :(得分:2)

因为它允许从右值进行初始化,所以很糟糕。

答案 5 :(得分:0)

实际上,复杂的函数可以简化为两个简短的函数,例如

int& foo(int x){
   return x;
}
int generate_a_int(){
   return 42;
}

然后调用它foo(generate_a_int()),生成一个临时值,一旦离开函数主体,generate_a_int()生成的临时值将被销毁,然后发生悬挂引用...

你现在明白了吗?