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
是悬而未决的参考。
有人可以解释吗?
答案 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()生成的临时值将被销毁,然后发生悬挂引用...
你现在明白了吗?