"Intrusive" is C++ speak. The regular linked lists always had embedded data or a mix of embedded data and pointers to outside data in a C struct.
"Intrusive" is C++ speak. The regular linked lists always had embedded data or a mix of embedded data and pointers to outside data in a C struct.
Now with std::list and college classes often teaching non-intrusive linked lists, and intrusive lists only being used in deep dark places like the OS kernel, maybe it’s easy to assume the ‘regular’ kind is non-intrusive.
What Stroustrup called ‘intrusive’ had been the default understanding of linked lists since around 1955, and what people used most often. A ‘regular’ linked list to most people back then was the intrusive kind, and the term ‘non-intrusive’ might have been an attempt to sell people on the benefits of abstracting and separating node types from payloads, but that maybe papers over the disadvantages a little.
The only kind of linked list I’ve ever used in my professional career is the intrusive kind. There are very few good reasons to ever use non-intrusive lists outside of the classroom. At least, not if you care about performance at all. They might be convenient & easy, but it’s usually the case that either an array or an intrusive list would be a better engineering choice.
(Actually, in many cases arrays are the default (like ArrayList in Java), because lists tend to only get assembled once and then passed around without further modification.)
If you mean being able to a generic `list<foo>` type (whether by C++ templates, macros, or good old void* casts), it's more than that. The benefit of non-intrusive is being able to create/manipulate/pass around multiple collections pointing to same payloads, without disturbing the payloads or the other collections in any way¹. That makes it easier to return and manipulate collections functionally (both in the narrow immutable sense, but also in the wider "treat collections like _values_ sense)... The deepest benefit arising from that I suppose is code modularity: different code areas need not be aware of each other's existence.
That all obviously comes at some tradeoff to performance. By definition, not having a full picture of the pathways your data travels means you can't choose the fastest representation!
¹The cheat is GC. For multiple unconnected collections to point to same payload, you need ref counting, or mark&sweep or similar to control its lifetime. Technically, GC does disturb the objects it's tracking (though that's well abstracted from other code).
However, you're largely right that specifically linked lists are rarely a good choice for non-intrusive collections => Arrays usually beat them, and if not then hash sets. (LISP & ML & Haskell do stick to lists for tail sharing — a choice which is arguably outdated by growing CPU / mem random access gap. I suppose Clojure's persistent vectors are an improvement.)
In the C++ world the STL introduced generic data types such as linked lists, which became the default, but "instrusive" linked lists still have their place in specialized list-heavy use cases where performance matters. In a previous job I wrote a widely adopted XML/JSON library using instrusive lists to link child elements, and the performance benefit was considerable, with my DOM API basically hiding this implementation detail from the user.
The article is wrong too, or at least using the term over-specifically.
It's not really tied to C++isms at all.
"Intrusive" got popular with C++ intrusive pointers, and that is where the article gets is misinformation from.
And of coursed the web jockeys downvote the correct objection since they have no clue about data structures, history, logic or basic reading skills.
It got popular with C++'s attempts at type safety. In particular, std::list lets you accomplish the machinery without macros, and allowing for polymorphism (heterogeneous lists of derived instances) without weird type casts and overallocation tricks, but at the cost of another level of indirection.