2

I just started learning the new C++ memory model:

#include <string>
#include <iostream>
#include <memory>

void print(unique_ptr<std::string> s) {
        std::cout << *s << " " <<  s->size() << "\n";
}

int main() {
        auto s = std::make_unique<std::string>("Hello");
        print(std::move(s));
        std::cout << *s;
        return 0;
}

Right now calling cout << *s; results in a segfault, as it should. I understand why it happens. But I also would like to know if there's a way get back the ownership. I'd like to be able to use a value after passing it to a function.

Alex
  • 34,581
  • 26
  • 91
  • 135
  • 3
    If you `std::move` it, then you are giving it away. If you want to keep it, then don't use `std::move`. Pass by reference. – Raymond Chen May 09 '19 at 22:20
  • ^ that being said, even if you decided to transfer the ownership of `s`, `std::move` may not be the best way. Take a look at `unique_ptr`'s methods in the documentation – Sweeney Todd May 09 '19 at 22:23
  • 2
    There is no such thing as code that "should result in a segfault". Incorrect code like this is described as having Undefined Behavior, which means the Standard makes no guarantees on what a decent compiler should be expected to do. So it might print "(null)", might do nothing at all, might give you nasal daemons. (Maybe once you start looking beyond the Standard to specific compilers and architectures, but then you're banking a lot on optimization choices, which can frequently change.) – aschepler May 09 '19 at 22:24
  • a) `unique_ptr` as an argument means: i'll transfer you this string. This is hardly what you want as an argument of a function. You *should not* use `unique_ptr` for arguments unless really needed. b) Use a reference, and if you're not modifying it, a const reference. `const string&` means: i'll pass you a string that *exists* (references can't be null, no null check needed) and it's *mine* (ownership remains on the caller) and you shouldn't modify it. `string&` means: it's mine, but you can touch. c) Standard pointer when it can be null (i.e. optional parameters). – Mirko May 10 '19 at 00:09
  • @aschepler Specific platforms (POSIX) do define certain things that result in segfaults. (I haven't actually checked the POSIX standard though) – user253751 May 10 '19 at 01:53

1 Answers1

7

If you don't want to transfer ownership of the owned object, then don't pass the unique_ptr to the function. Instead, pass a reference or a raw pointer to the function (in modern C++ style, a raw pointer is usually understood to be non-owning). In the case where you just want to read the object, a const reference is usually appropriate:

void print(const std::string&);
// ...
print(*s);
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • I see, thanks. But I read that I should use unique pointers for everything. This works: `void print(unique_ptr &s)`. So when should I use `unique_ptr` vs `unique_ptr&` vs `string&`? – Alex May 09 '19 at 22:24
  • 9
    @Alex You *shouldn't* use `unique_ptr` everywhere. You *should* ensure that every dynamically allocated resource is owned by an RAII object, such as `unique_ptr` or a container, that will deallocate it when it goes out of scope. If you have an object that is owned by a `unique_ptr` then it is fine to let it be "observed" through a raw pointer or reference. – Brian Bi May 09 '19 at 22:27
  • @Alex Some help with terminology: [What is ownership of resources or pointers?](https://stackoverflow.com/questions/49024982/what-is-ownership-of-resources-or-pointers) – user4581301 May 09 '19 at 22:51