Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-Movable C++17 Unique Pointer

I came across this answer Prevent moving of a unique_ptr C++11. However while trying it out on a compiler online, this works with C++11(std::move compiler error) but with C++17, I'm seeing that the std::move below is successful. Shouldn't the compiler throw an error on that line? Also if some semantics have changed in C++17, what is the correct way to create a non movable unique_ptr in C++17 and onward.

template <typename T>
using scoped_ptr = const std::unique_ptr<T>;

int main()
{
    auto p = scoped_ptr<int>(new int(5));
    auto p2 = std::move(p); // should be error?
    std::cout << *p2 << std::endl; // 5
    return 0;
}

You can try it online here.

like image 655
tangy Avatar asked Nov 24 '18 06:11

tangy


People also ask

Can Unique pointers be moved?

As you may know, a smart pointer is a class that provides several methods and features. For example you can count the references of a std::shared_ptr or increase them by making a copy; you can move data from a std::unique_ptr to another one (change of ownership); you can empty a smart pointer and so on.

Can unique_ptr be copied?

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.

What is the use of unique pointer?

Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.

Why should I use Make_unique?

Using the 'make_unique' function to create the pointer helps solve this problem by guaranteeing the freeing of memory if an exception occurs. The C++17 standard, while still not specifying the exact evaluation order for arguments, provides additional guarantees.


2 Answers

p is not const. See here for it to fail the way you expect.

auto deduces like a template<class T>void foo(T) does. T is never deduced as const, and neither is auto p=.

Meanwhile, the auto p = line works because you compiled it in c++17 mode. In c++11 it does not compile. This is because how prvalues differ in 17; some call the difference guaranteed elision.

If you want an immobile unique ptr:

template<class T, class D>
struct immobile_ptr:private std::unique_ptr<T, D>{
  using unique_ptr<T>::operator*;
  using unique_ptr<T>::operator->;
  using unique_ptr<T>::get;
  using unique_ptr<T>::operator bool;
  // etc

  // manually forward some ctors, as using grabs some move ctors in this case
};
template<class T, class...Args>
immobile_ptr<T> make_immobile_ptr(Args&&...args); // todo

an alternative might be to take a unique ptr with an immobile destroyer.

template<class X>
struct nomove_destroy:std::destroy<T>{
  nomove_destroy(nomove_destroy&&)=delete;
  nomove_destroy()=default;
  nomove_destroy& operator=(nomove_destroy&&)=delete;
};
template<class T>
using nomove_ptr=std::unique_ptr<T,nomove_destroy<T>>;

But I am uncertain if that will work.

like image 126
Yakk - Adam Nevraumont Avatar answered Sep 25 '22 13:09

Yakk - Adam Nevraumont


Note that p is declared as non-reference type, the const part of the argument scoped_ptr<int>(new int(5)) is ignored in type deduction. Then the type deduction result for p is std::unique_ptr<int>, not const std::unique_ptr<int> (i.e. scoped_ptr<int> as you expected).

What you want might be

auto& p = scoped_ptr<int>(new int(5)); // p is of type const std::unique_ptr<int>& now
like image 37
songyuanyao Avatar answered Sep 21 '22 13:09

songyuanyao