32

I was always assuming that std::move() on a std::shared_ptr steals the pointer and sets the pointer of the original to nullptr-thus not increasing the reference count. That does not seem to be true in my world.

SETUP:

MacOS, g++ -version => "Apple LLVM version 10.0.1 (clang-1001.0.46.3)"

CODE:

#include <cstdio>                                                                                                                                                                                  
#include <memory>
class Thing { public: Thing(int N) : value(N) {} int value; };

void print(const char* name, std::shared_ptr<Thing>& sp)
{ printf("%s: { use_count=%i; }\n", name, (int)sp.use_count()); }

int main(int argc, char** argv) {
    std::shared_ptr<Thing>  x(new Thing(4711));
    print("BEFORE x", x);
    std::shared_ptr<Thing>  y = std::move(x);
    y->value = 4712;
    print(" AFTER x", x);
    print(" AFTER y", y);
    return 0;
}

OUTPUT:

Compiling (g++ tmp.cpp -o test), and running (./test), delivers

BEFORE x: { use_count=1; }
 AFTER x: { use_count=2; }
 AFTER y: { use_count=2; }

So, the reference count is increased while using std::move().

QUESTION:

What is going on, here?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Frank-Rene Schäfer
  • 3,182
  • 27
  • 51

1 Answers1

43

What is going on, here?

On MacOS, it seems that you must explicitly enable move-sematics with -std=c++11 (or later standards)¹. Otherwise, the example happens to compile (i.e., std::shared_ptr from the related library implementation is usable) but doesn't work correctly as the required language features aren't enabled. This results in actual copies being made instead of move constructions. It would have been better if the AppleClang package didn't even allow an instantiation of std::shared_ptr when the required language features isn't enabled.

¹) Thanks to @t.niese for testing the given compiler/platform.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • 1
    @lubgr I can confirm that your code will result on `use_count=2;` when compiled and run with the given compiler on mac, but will result in `use_count=1;` on e.g. wandbox. – t.niese Sep 09 '19 at 10:43
  • 2
    @lubgr but only when compiling with `g++ tmp.cpp -o test`, with `g++ -std=c++11 tmp.cpp -o test` or `g++ -std=c++17 tmp.cpp -o test` it shows `use_count=1;` – t.niese Sep 09 '19 at 10:47
  • 2
    Sounds like the standard library in use allows for using C++11 features (`std::shared_ptr`) without enabling the language support (move semantics), which results in actual copies being made. – lubgr Sep 09 '19 at 10:49
  • @lubgr; your last comment seems to pinpoint into the right direction. Can you, or someone, make an answer out of it? – Frank-Rene Schäfer Sep 09 '19 at 10:52
  • @lubgr by default it compiles with pre `c++11`. So yes the `std::move` does nothing and only a copy is created. – t.niese Sep 09 '19 at 10:53
  • That'll be the real reason (the UB is true but a distraction and won't be causing this). Annoying that you can still use `shared_ptr` in C++03 mode! – Lightness Races in Orbit Sep 09 '19 at 10:53
  • @lubr: I edited my question so that the print function only prints the reference count, because that was the core issue. You might want to remove the first part of your question. – Frank-Rene Schäfer Sep 09 '19 at 11:05
  • Re: "It would have been better if the AppleClang package didn't even allow an instantiation of `std::shared_ptr` when the required language features isn't enabled": I don't understand this. `shared_ptr` isn't like `unique_ptr`, where it makes no sense without rvalue references and move semantics; rather, it seems like `shared_ptr` works 100% perfectly, and serves its intended purpose, even without those language features. Granted, you'll lose some optimizations; but why should the lack of those optimizations be an error? – ruakh Sep 10 '19 at 06:51
  • @ruakh I think when people learn about `std::shared_ptr`, they do so in conjunction with move semantics. Having a `std::shared_ptr` without those might work, but causes lots of confusion. I'm also having difficulties in seeing the real-world scenario where you'd want to use a compiler/library that provides smart pointer, but still stick to C++98. But admittedly, I do see your point too... – lubgr Sep 10 '19 at 06:56
  • @lubgr: Re: "I'm also having difficulties the real-world scenario where you'd want to use a compiler/library that provides smart pointer, but still stick to C++98": FWIW, `boost::shared_ptr` predated C++03 by several years, so if you're curious, you can probably find Boost documentation from back then and see what kind of use-cases it was recommended for back when *everyone* still stuck to C++98. (You'll probably find that it's exactly the same scenarios where `std::shared_ptr` is used today. The move semantics don't really change it.) – ruakh Sep 10 '19 at 07:15