использование shared_ptr для std :: vector в основанном на диапазоне цикле for - PullRequest
0 голосов
/ 30 апреля 2018

Я написал функцию c ++, которая собирает некоторые данные, а затем возвращает std::shared_ptr во вновь распределенную std::vector, содержащую данные. Нечто аналогичное этому:

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;
}

Я попытался перебрать содержимое вектора, используя цикл for на основе диапазона, но он действовал так, как будто вектор был пустым. Поигравшись, я обнаружил, что могу заставить его вести себя так, как я ожидал, присваивая значение, возвращаемое функцией, локальной переменной, а затем ссылаясь на это в цикле:

// 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;

Отрезанный печатает это:

First loop:
Second loop:
1
2
3

Почему не работает первая версия?

Я использую Xcode в macOS Sierra 10.12.6. Я считаю, что он использует LLVM 9.0 для компиляции кода C ++.

1 Ответ

0 голосов
/ 30 апреля 2018

Обратите внимание, что shared_ptr_to_std_vector_of_ints возвращает по значению, поэтому то, что он возвращает, является временным.

Диапазон для цикла эквивалентен

{
  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>, затем связывает его с rvalue-reference __range. Временный std::shared_ptr<std::vector<int>> будет уничтожен после полного выражения, а use_count уменьшится до 0, поэтому управляемый std::vector<int> также будет уничтожен. Тогда __range становится привязанным. После этого, например auto __begin = begin_expr ; попытается получить итератор от __range, что приведет к UB.

(акцент мой)

Если range_expression возвращает временное значение, его время жизни увеличивается до конца цикла, как указано привязкой к rvalue-ссылке __range, но следует помнить, что время жизни любого временного объекта в range_expression не продлевается.

Как показала ваша 2-я версия, проблему можно решить, используя вместо этого именованную переменную; или вы также можете использовать оператор init (из C ++ 20):

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