61

I'm trying to figure out how/if I can use unique_ptr in a queue.

// create queue
std::queue<std::unique_ptr<int>> q;

// add element
std::unique_ptr<int> p (new int{123});
q.push(std::move(p));

// try to grab the element
auto p2 = foo_queue.front();
q.pop(); 

I do understand why the code above doesn't work. Since the front & pop are 2 separate steps, the element cannot be moved. Is there a way to do this?

Ilia Choly
  • 18,070
  • 14
  • 92
  • 160

1 Answers1

93

You should say explicitly that you want to move the pointer out of the queue. Like this:

std::unique_ptr<int> p2 = std::move(q.front());
q.pop();
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 3
    @iliacholy: loosely speaking, `q.front()` returns an *l-value*, so you cannot copy-initialize `p2` with it because `unique_ptr`s are not copyable. `std::move` converts it to an *r-value*, so now `p2` is being move-initialized instead, which is allowed by design. – Yakov Galka Jan 02 '13 at 21:33
  • I didn't think that `front` would be allowed to return it as an `l-value`. – Ilia Choly Jan 02 '13 at 21:42
  • @iliacholy: Why not? It returns a non-const reference to the element withing the queue. – Yakov Galka Jan 03 '13 at 07:10
  • Ah ok, that makes sense! For some reason I thought the `unique_ptr` was being returned by value. – Ilia Choly Jan 03 '13 at 14:17
  • 28
    Also important is that after the `move`, the top element in the `queue` is a `unique_ptr` equal to `nullptr`. The pop is needed to remove this "empty" `unique_ptr` from the `queue`. – rubenvb Jan 03 '13 at 14:24
  • 1
    why wouldn't pop() just return the top item as an rvalue, I wonder? – Mordachai Jul 08 '13 at 20:06
  • 4
    @Mordachai: exception safety requires it. – moswald Jul 09 '13 at 02:48
  • 1
    @rubenvb it is not required to be null, strictly speaking. After move-constructing from it, it's just an invalid object, not owning a resource. Of course that is most easily implemented by making it null – Arne Mertz Jul 09 '13 at 06:23
  • 1
    @Mordachai: efficiency. Sometimes you can just as well use the object in the queue through the reference returned by `front()` and then delete and forget about it. Also in C++03 you could not implement an efficient return by value for `pop()`. – Yakov Galka Jul 09 '13 at 09:15
  • @Mordachai Exception safety: it is to prevent losing the element from the queue in the case the copy construction involved in the return throws an exception. – juanchopanza Jul 10 '13 at 15:30
  • 1
    All of which combine to make it a terrible interface for a smarter-pointer based stack or queue. Interesting, but C++ constantly makes pointed-to vs. by-copy require very different semantics to make things work well, IMO. – Mordachai Jul 11 '13 at 13:56