RAII-style single-linked list After watching Herb Sutter describe single-linked lists in terms of unique_ptr I decided to implement my own. In particular I want to know if my move semantics are correct and if any unnecessary copies are made. It passes my simple cases with int and I plan to add insertion/deleting later.

#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;cstddef&gt;
#include &lt;utility&gt;

template&lt;typename Type&gt;
class Node {
public:
  Node(const Type&amp; data) : data {data} {}
  std::unique_ptr&lt;Node&lt;Type&gt;&gt; next = nullptr;
  Type data;
};

template&lt;typename Type&gt;
class LinkedList {
public:
  void push_back(const Type&amp; data) {
    if (!head) {
      head = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
      return;
    }

    Node&lt;Type&gt;* end = head.get();

    while (end-&gt;next) {
      end = end-&gt;next.get();
    }

    end-&gt;next = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
  }

  Type pop_back() {
    if (!head) {
      throw;
    }

    Node&lt;Type&gt;* end = head.get();
    Node&lt;Type&gt;* previous = nullptr;

    while (end-&gt;next) {
      previous = end;
      end = end-&gt;next.get();
    }

    const Type data = end-&gt;data;

    if (previous) {
      previous-&gt;next = nullptr;
    }

    return data;
  }

  void push_front(const Type&amp; data) {
    if (!head) {
      head = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
      return;
    }

    std::unique_ptr&lt;Node&lt;Type&gt;&gt; new_head = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
    new_head-&gt;next = std::move(head);
    head = std::move(new_head);
  }

  Type pop_front() {
    if (!head) {
      throw;
    }

    const Type data = head-&gt;data;
    head = std::move(head-&gt;next);
    return data;
  }

  std::size_t size() const {
    if (!head) {
      return 0;
    }

    std::size_t size = 1;
    Node&lt;Type&gt;* traverse = head.get();

    while (traverse-&gt;next) {
      traverse = traverse-&gt;next.get();
      size++;
    }

    return size;
  }

  /* iterative destroy to avoid recursive deletes */
  void destroy() {
    if (!head) {
      return;
    }

    while (head-&gt;next) {
      head = std::move(head-&gt;next);
    }

    head = nullptr;
  }

private:
  std::unique_ptr&lt;Node&lt;Type&gt;&gt; head = nullptr;
};


Edit: With Deduplicator's response I ended up with this code

#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;cstddef&gt;
#include &lt;utility&gt;

template&lt;typename Type&gt;
class Node {
public:
  Node(const Type&amp; data) : data {data} {}
  std::unique_ptr&lt;Node&lt;Type&gt;&gt; next = nullptr;
  Type data;
};

template&lt;typename Type&gt;
class LinkedList {
public:
  void push_back(const Type&amp; data) {
    auto end = &amp;head;

    while (*end) {
      end = &amp;(*end)-&gt;next;
    }

    *end = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
  }

  Type pop_back() {
    auto end = &amp;head;
    auto previous = &amp;head;

    while (*end) {
      previous = end;
      end = &amp;(*end)-&gt;next;
    }

    const Type data = (*previous)-&gt;data;
    *previous = nullptr;
    return data;
  }

  void push_front(const Type&amp; data) {
    auto new_head = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
    new_head-&gt;next = std::move(head);
    head = std::move(new_head);
  }

  Type pop_front() {
    const Type data = head-&gt;data;
    head = std::move(head-&gt;next);
    return data;
  }

  ~LinkedList() {
    auto end = &amp;head;

    while (*end) {
      *end = std::move((*end)-&gt;next);
    }
  }

private:
  std::unique_ptr&lt;Node&lt;Type&gt;&gt; head = nullptr;
};
Almost always auto. Only write down the specific type when you have to, it's shorter, clearer, and less error-prone.

You currently handle an empty list as a special case. Don't, it's neither efficient nor elegant.
Instead, use a pointer to pointer, and everything is simplified. As an example:

void push_back(const Type&amp; data) {
    auto p = &amp;head;
    while (*p)
        p = &amp;(*p)-&gt;next;
    *p = std::make_unique&lt;Node&lt;Type&gt;&gt;(data);
}


Good of you to avoid recursive descent in the dtor. But, you know that's not spelled destroy()?