1

In below code snippet i am getting segmentation fault while passing unique_ptr as as value. usually this is known issue with auto_ptr as due to ownership issue (Assignee pointer becomes NULL) it can't be accessed after assignment. but why i am facing the same issue with unique_ptr even though it is having move semantics.

what i understood auto_ptr use copy constructor and unique_ptr use move function to transfer the ownership. But in both cases assignee pointer becomes null then what is the significance of having move semantics here. I believe compiler should flash the error if unique_ptr is passed as a value like below statement as copy constructor is private in unique_ptr. could you throw some light on this regarding this behaviour.

unique_ptr<Test> a = p;//deleted function (copy constructor).

Here is the code snippet:

#include<iostream>

#include<memory>

using namespace std;

class Test
{
public:
    Test(int a = 0 ) : m_a(a)
    {
    }
    ~Test( )
    {
        cout<<"Calling destructor"<<endl;
    }

    int m_a;
};


//***************************************************************
void Fun(std::unique_ptr<Test> p1 )
{
    cout<<p1->m_a<<endl;
}
//***************************************************************
int  main( )
{
    std::unique_ptr<Test> p( new Test(5) );
    Fun(std::move(p));
    cout<<p->m_a<<endl;

    return 0;
}
RamblingMad
  • 5,332
  • 2
  • 24
  • 48
user2997518
  • 812
  • 6
  • 17
  • you may still access the same by passing reference . – user2907032 Aug 02 '15 at 08:19
  • 3
    With `unique_ptr` and `move`, your have to be *explicit* about the ownership transfer. – Jarod42 Aug 02 '15 at 08:25
  • In [tag:Rust], similar code would produce compiler error :-) – Jarod42 Aug 02 '15 at 08:25
  • and with unique_ptr ownership is handled compile-time. – Jens Munk Aug 02 '15 at 08:27
  • 2
    You are moving ownership of your object into the function (indicated by the explicit move), leaving the original pointer empty (nullptr) and then you dereference that pointer. What did you expect would happen and why are you passing a unique ptr into the function at all? A normal or would do just fine. – MikeMB Aug 02 '15 at 08:36

3 Answers3

3

By writing std::move(p) you are casting p to an r-value reference. unique_ptr has a constructor that takes an r-value reference (a move constructor) so it constructs the by-value parameter using this move constructor and there is no compilation error.

You should not pass a unique_ptr by value unless you want to transfer ownership.

If you transfer ownership then the unique_ptr in the outer scope does not own anything anymore and it is undefined behaviour to dereference it.

In this case you should pass Test by const reference:

void fun(const Test& t) {
    std::cout << t.m_a << std::endl;
} 

int main(){
    auto p = std::make_unique<Test>(5);
    fun(*p);
}

See here for more information about how to pass smart pointers in general.

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • +1 for this explanation. I am just curious to know, why can't we achieve the same by using auto_ptr ( deprecated now ). It seems to be during copy construction RHS assignee object was setting NULL. – user2997518 Aug 03 '15 at 09:11
  • 1
    @user2997518 I'm not that familiar with `auto_ptr` but my understanding is that because it was implemented before move-semantics existed they implemented the "move" behaviour as part of copy. This is not an ideal solution because it makes it hard to use `auto_ptr` in generic code that expects normal copy-semantics but they can't change it now or it would break lots of existing code. So they chose to deprecate `auto_ptr` instead of updating it to properly use move semantics. You probably can achieve the same with `auto_ptr` if you are very careful but it is much clearer with `unique_ptr`. – Chris Drew Aug 03 '15 at 09:54
  • below link is pretty useful which talks about r-value reference (&&operator) . http://stackoverflow.com/questions/3106110/what-are-move-semantics – user2997518 Aug 03 '15 at 12:56
1

The deleted copy constructor is not involved here. unique_ptr has a move constructor which is used, because you explicitly requested a move from the source with std::move.

You shouldn't be using the moved-from value except to destroy it out assign to it.

It's not an error. A compiler could warn, but since you have explicitly moved it may reasonably assume that you know what you are doing.

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
0

Instead of using unique_ptr as a value , pass the same as a reference . You create a unique pointer. You then construct another unique_ptr from that one, using the move constructor. So the original gets set to null. That's what happens when you move a unique_ptr. Honestly speaking as i didn't get chance work with auto_ptr so can't comment on this behavior.

void Fun( const std::unique_ptr<Test>& p1 )
{
 cout<<p1->ma;
}
user2907032
  • 434
  • 3
  • 13
  • 3
    Better to pass directly `const Test&` (or `const Test*`) see [smart_pointer_parameters](http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/) – Jarod42 Aug 02 '15 at 08:33