Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::shared_ptr not working with range for

I'm trying to iterate over a temporary object in a range for loop. It looks like the object gets desctucted before the loop begins executing. Is this a standard compliant behaviour? I'm using gcc 4.8.

#include <iostream>
#include <vector>
#include <memory>

struct Test: std::vector<int> {
  Test(): std::vector<int>{1,2,3} {
    std::cout << __PRETTY_FUNCTION__ << '\n';
  }

  ~Test() {
    std::cout << __PRETTY_FUNCTION__ << '\n';
  }
};

std::shared_ptr<Test> func() {
  return std::shared_ptr<Test>(new Test);
}

int main() {
  for (const auto &obj: *func()) {
    std::cout << obj << '\n';

  }
}

The result is as follows:

Test::Test()
Test::~Test()
21770300
0
33
0
0
0
3
like image 525
kyku Avatar asked Apr 26 '13 16:04

kyku


1 Answers

Yes, the behavior is compliant.

Per paragraph 6.5.4/1 of the C++11 Standard:

For a range-based for statement of the form

for ( for-range-declaration : expression ) statement

let range-init be equivalent to the expression surrounded by parentheses

( expression )

and for a range-based for statement of the form

for ( for-range-declaration : braced-init-list ) statement

let range-init be equivalent to the braced-init-list. In each case, a range-based for statement is equivalent to

{
    auto && __range = range-init;
    for ( auto __begin = begin-expr,
        __end = end-expr;
        __begin != __end;
        ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

In your case, the returned shared pointer is dereferenced, and the object it points to is bound to the __range reference. However, the shared pointer itself is not copied, nor is it bound to a reference that would prolong its lifetime. Therefore, it goes out of scope. Being the last shared pointer referencing the pointed object, the object gets destroyed too.

Things would have been different if you had returned your Test object by value, rather than returning a shared pointer:

Test func() {
  return Test();
}

int main() {
  for (const auto &obj: func()) {
    std::cout << obj << '\n';

  }
}

This way, the Test temporary returned by func() is bound to the __range reference, and its lifetime is prolonged to match the lifetime of the reference.

Here is a live example.

like image 58
Andy Prowl Avatar answered Sep 21 '22 23:09

Andy Prowl