Building a simple singly-linked list in C++11 The implementation below works, but I'm wondering if there's a modern C++ alternative.

For example:


Should I replace the raw new with a braced initializer?
Should I replace the Node* with a shared_ptr?


Let me know if you see any other things I could improve. I know I could also use a class, but I'd like to understand how to do this with struct.

#include &lt;iostream&gt;

struct Node {
  int data;
  Node* next;
};

void insert(Node*&amp; head, int data) {
  Node* new_node = new Node;
  new_node-&gt;data = data;
  new_node-&gt;next = head;
  head = new_node;
}

void print_list(Node* list) {
  Node* p {list};
  std::cout &lt;&lt; "Printing list: " &lt;&lt; std::endl;
  if (p) {
    std::cout &lt;&lt; p-&gt;data;
    p = p-&gt;next;
    while(p) {
      std::cout &lt;&lt; " -&gt; " &lt;&lt; p-&gt;data;
      p = p-&gt;next;
    }
    std::cout &lt;&lt; std::endl;
  }
}

int main(int argc, char* argv[]) {
  Node* my_list {};
  insert(my_list, 10);
  insert(my_list, 20);
  print_list(my_list);
}
// Printing list: 
// 20 -&gt; 10
First, note that the only real difference between a struct and a class is the default accessibility: in a class everything is private by default, and in a struct everything is public by default. That's literally the only difference between the two.

In any case, some of your code can be simplified somewhat. For example, your insert can be reduced to just this:

void insert(Node*&amp; head, int data) {
    head = new Node{data, head};
}


It's usually better to initialize rather than construct something uninitialized, and then assign a value anyway, so this is sort of a double-win.

I'd also prefer to use a for loop to print the list, and just a new-line where you want to print a new-line:

void print_list(Node* list) {
  Node* p {list};
  std::cout &lt;&lt; "Printing list:\n";
  if (p)
  {
    std::cout &lt;&lt; p-&gt;data;
    for (p=p-&gt;next; p != nullptr; p=p-&gt;next)
      std::cout &lt;&lt; " -&gt; " &lt;&lt; p-&gt;data;
  }
  std::cout &lt;&lt; "\n";
}


In this case, I'd restructure the code a bit to treat the list being non-null as a pre-condition to print_list and enforce that up-front:

void print_list(Node* list) {
  if (list == nullptr)
      return;
  std::cout &lt;&lt; "Printing list:\n";
  std::cout &lt;&lt; list-&gt;data;
  for (Node *p{list-&gt;next}; p != nullptr; p=p-&gt;next)
    std::cout &lt;&lt; " -&gt; " &lt;&lt; p-&gt;data;
  std::cout &lt;&lt; "\n";
}


The next steps I can see would involve the sort of re-vamping you don't seem to want right now--turning the linked list into a full-blown class of its own that supports iterators and such. This does have a tremendous advantage though: right now, your linked list only supports inserting items, then printing an entire linked list. If anybody wants to do anything else, they need to implement it entirely on their own, and know all about the internal details of the linked list to do it.

Along with that, you'd probably want to make it a generic container, so you can use it to store any data type, not just int.

This way, you can immediately support essentially all code that knows how to work with a collection that supports (in this case) forward iterators. You'd probably just eliminate print_list entirely, as it becomes just an application of std::copy from a list to (for example) an infix_iterator.

This is (IMO) much cleaner in general. In particular, it separates concerns much more cleanly: the linked list can concentrate solely on linked-list "things", and not worry about things like I/O as it does now.