Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::move with std::shared_ptr in lambda

I have a strange issue when moving std::shared_ptrs in lambdas. I am not sure if it is a bug or not, as I can reproduce with g++ v6.3 and clang++ v3.9.

When I compile and run the following program:

#include <iostream>
#include <memory>

void f(std::shared_ptr<int> ptr) {
  std::cout << 3 << " " << ptr.get() << std::endl;
}

int main() {
  auto ptr = std::make_shared<int>(42);
  std::cout << 1 << " " << ptr.get() << std::endl;
#ifdef LAMBDA
  auto lambda = [ptr]() {
#endif
    f(std::move(ptr));
    std::cout << 2 << " " << ptr.get() << std::endl;
#ifdef LAMBDA
  };
  lambda();
#endif
}

with the command c++ -std=c++14 -o test main.cpp && ./test yields something like

1 0x55a49e601c30 1
3 0x55a49e601c30 1
2 0 0

However, changing the compilation command to c++ -std=c++14 -o test main.cpp -DLAMBDA makes the execution print something I cannot explain:

1 0x55a49e601c30 1
3 0x55a49e601c30 3
2 0x55a49e601c30 2

So, it appears that the std::move(move), when performed inside a lambda, does not actually cause the shared_ptr to be moved, nor does it prevent its reference counter to be incremented.

Again, I can reproduce this with clang++ and g++.

How is that possible?

like image 332
rems4e Avatar asked Apr 10 '17 09:04

rems4e


People also ask

What happens if you move a shared_ptr?

By moving the shared_ptr instead of copying it, we "steal" the atomic reference count and we nullify the other shared_ptr . "stealing" the reference count is not atomic, and it is hundred times faster than copying the shared_ptr (and causing atomic reference increment or decrement).

Is shared_ptr copyable?

The ownership of an object can only be shared with another shared_ptr by copy constructing or copy assigning its value to another shared_ptr .

What is shared_ptr?

The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory.

Where is std :: shared_ptr defined?

If your C++ implementation supports C++11 (or at least the C++11 shared_ptr ), then std::shared_ptr will be defined in <memory> . If your C++ implementation supports the C++ TR1 library extensions, then std::tr1::shared_ptr will likely be in <memory> (Microsoft Visual C++) or <tr1/memory> (g++'s libstdc++).


1 Answers

The captured variable ptr in a lambda is by default const, i.e. the type of ptr is const std::shared_ptr<int>.

std::move cannot move out const objects, so a copy is created instead.

If you really want to move it, the ptr must be allowed to be mutable:

auto lambda = [ptr]() mutable {
//                    ^~~~~~~
like image 130
kennytm Avatar answered Sep 22 '22 13:09

kennytm