在基于范围的for循环中使用shared_ptr到std :: vector

时间:2018-04-30 03:46:55

标签: c++ c++11 shared-ptr ranged-loops

我编写了一个c ++函数,它汇总了一些数据,然后将LoginUser返回给包含数据的新分配的std::shared_ptr。类似的东西:

std::vector

我尝试使用基于范围的for循环迭代向量的内容,但它的作用就好像向量是空的。摆弄后,我发现通过将函数返回的值赋给局部变量,然后在循环中引用它,我可以按照我的预期运行:

std::shared_ptr<std::vector<int>> shared_ptr_to_std_vector_of_ints()
{
    auto v = std::make_shared<std::vector<int>>();
    for (int i = 0; i < 3; i++) v->push_back(i);
    return v;
}

那剪断了这张照片:

// Executes loop zero times:
std::cout << "First loop:" << std::endl;
for (int i : *shared_ptr_to_std_vector_of_ints()) std::cout << i << std::endl;

// Prints three lines, as expected
std::cout << "Second loop:" << std::endl;
auto temp = shared_ptr_to_std_vector_of_ints();
for (int i : *temp) std::cout << i << std::endl;

为什么第一个版本不起作用?

我在macOS Sierra 10.12.6上使用Xcode。我相信它正在使用LLVM 9.0来编译c ++代码。

1 个答案:

答案 0 :(得分:7)

请注意shared_ptr_to_std_vector_of_ints按值返回,因此返回的是临时值。

range-based for loop相当于

{
  init-statement
  auto && __range = range_expression ; 
  auto __begin = begin_expr ;
  auto __end = end_expr ;
  for ( ; __begin != __end; ++__begin) { 
    range_declaration = *__begin; 
    loop_statement 
  } 
} 

部分auto && __range = range_expression ;,对于您的示例,它将是auto && __range = *shared_ptr_to_std_vector_of_ints() ;shared_ptr_to_std_vector_of_ints会返回一个临时std::shared_ptr<std::vector<int>>,然后取消引用它以获取std::vector<int>,然后将其绑定到右值引用__range。完整表达式后,临时std::shared_ptr<std::vector<int>>将被销毁,use_count减少到0,因此管理的std::vector<int>也将被销毁。然后__range成为悬空参考。之后,例如auto __begin = begin_expr ;会尝试从__range获取迭代器,从而导致UB。

(强调我的)

  

如果range_expression返回一个临时值,它的生命周期会延长到循环结束,如绑定到右值引用__range所示,但要注意range_expression中任何临时值的生命周期都不会延长

正如您的第二个版本所示,问题可以通过使用命名变量来解决;或者您也可以使用init-statement(来自C ++ 20):

for (auto temp = shared_ptr_to_std_vector_of_ints(); int i : *temp) std::cout << i << std::endl;