Search code examples
c++moveshared-ptrmove-semantics

std::move and std::shared_ptr argument passed by value


Consider following code:

#include <iostream>
#include <memory>

class Test
{
    public:
    Test(int t) : t(t) {}
    
    int t;
};

void test(std::shared_ptr<Test> t)
{
    std::cout << t.use_count() << std::endl;
}

int main()
{
    auto t = std::make_shared<Test>(1);
    test(std::move(t)); //(1)
    test(std::make_shared<Test>(2)); //(2)
}

Questions:

  1. What ctor is executed in case (1) - copy or move?
  2. In case (2) temporary is created, then copy ctor is executed to create t in void test(std::shared_ptr<Test> t), then temporary is destroyed. Is that correct?

Solution

  • There is a total of 3 std::shared_ptr<Test> created in your example:

    1. auto t = std::make_shared<Test>(1);

    2. test(std::move(t)); - The receiving t in test() is move-constructed.

    3. test(std::make_shared<Test>(2)); - The receiving t is the one created by make_shared(). It's the same object because of mandatory copy/move-elision since C++17.

      Prior to C++17, you could in theory get as many as 5 instantiations - but since this kind of elision was allowed (but not mandatory) in the previous version, you'll most probably only get 3 in those versions too. When using one of those earlier versions you can turn off this optimization in some compilers (g++/clang++ for example) with -fno-elide-constructors. In C++17 and later, the elision is mandatory and can therefore not be turned off.


    Here's a demo which doesn't use shared_ptr but a test class called foo so that that you can track each instantiation. Note how there is only one foo with the value 2, which corresponds to your test(std::make_shared<Test>(2)); call.