What is going on: C++ std::move on std::shared_ptr increases use_count?
Asked Answered
A

1

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?

Arleta answered 9/9, 2019 at 10:29 Comment(2)
After fixing UB can't reproduce.Brasher
FWIW I'm on (almost) the same version and the library code doesn't match the report iff _LIBCPP_HAS_NO_RVALUE_REFERENCES isn't definedExpiation
H
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.

Hammad answered 9/9, 2019 at 10:33 Comment(10)
@Hammad 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.Gunny
@Hammad 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;Gunny
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.Hammad
@lubgr; your last comment seems to pinpoint into the right direction. Can you, or someone, make an answer out of it?Benjaminbenji
@Hammad by default it compiles with pre c++11. So yes the std::move does nothing and only a copy is created.Gunny
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!Expiation
@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.Benjaminbenji
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?Selfassertion
@Selfassertion 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...Hammad
@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.)Selfassertion

© 2022 - 2024 — McMap. All rights reserved.