9

Since STL containers require that all contents be copyable and assignable, what is the prefered idiom when working with non copyable objects?

I can think of two different approaches:

  1. Store (smart) pointers rather than the objects in STL containers.

  2. Get rid of STL containers and implement my own lists (e.g. each object must include a pointer to the next object).

Main drawback of the second approach is implementation of destructors (should the "next" object be destroyed before the current one in a recursive way?)

jbgs
  • 2,795
  • 2
  • 21
  • 28

2 Answers2

6

Since STL containers require that all contents be copyable and assignable, what is the prefered idiom when working with non copyable objects?

Well, actually with C++11 they require the object to be Movable. Only certain operations require them to be Assignable thanks to emplace_* methods.

I can think of two different approaches:

  1. Store (smart) pointers rather than the objects in STL containers.

  2. Get rid of STL containers and implement my own lists (e.g. each object must include a pointer to the next object).

The two approaches are certainly feasible.

In C++11, the STL containers with std::unique_ptr<YourObject> elements in probably the best option. It's standard all the way down. There might be a slight performance issue with the node-based containers since the node and the element they point to will be distinct memory areas; but it's generally imperceptible.

If it is perceptible, or if you cannot use C++11, then you should learn about intrusive containers, which consist in augmenting your objects with hooks so that they can arrange themselves into lists, for example. There is a Boost library for this, obviously: Boost.Intrusive.

Non Movable you said ?

I would honestly challenge most of the designs that argue that an object should not be moved. The only issue with moving is linked to object identity and the potential issues of invalidating pointers to the object being moved from (and thus living dangling pointers behind). This can generally be solved by smart pointers and/or Factory approaches.

Community
  • 1
  • 1
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • "I would honestly challenge most of the designs that argue that an object should not be moved." The most common case I see this in is for Thread objects whose running thread has captured the `*this` pointer. – eatnumber1 May 23 '23 at 09:19
  • @eatnumber1: Such a design tends to be brittle, though, as the slightest maintenance operation which leads to moving the object will break it. Hence my advice -- in the rest of the paragraph -- that if an object should not be moved, it is best to ensure so _structurally_, for example by putting it in a `std::unique_ptr`. Then the pointer can be moved, when the object remains stable. – Matthieu M. May 23 '23 at 09:54
  • unique_ptr makes it easy to work with an immovable object… which kind of makes my point that immovable objects are useful in some cases? – eatnumber1 May 24 '23 at 11:05
5

I'd choose approach #1: i.e. store smart pointers to objects in STL containers.

Note that it's fine to store non-owning raw pointers in STL containers (e.g. observing raw pointers), but storing owning raw pointers is a "leaktrocity": use shared_ptr or new C++11's unique_ptr instead.

As for #2, writing your own containers from scratch requires lots of time and energy, and I believe you can't match the richness of a full commercial-quality STL library implementation in a reasonable time-frame.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • I would like to see the term "leaktrocity" a bit more explained. Do you mean safety? – TobiMcNamobi Apr 16 '13 at 11:22
  • Well, I didn't mean implementing my own containers. I meant just implementing my own linked lists "in the C way". Anyway, first approach seems a better one. – jbgs Apr 16 '13 at 11:24
  • 1
    @user1916893: It means that you have to use it very carefully to avoid memory leaks. You have to remember that the raw pointers own objects, and simply removing or replacing them will leak the owned object. Smart pointers manage the ownership explicitly, in a way that can be checked by the compiler; it's difficult to leak without making a conscious effort. – Mike Seymour Apr 16 '13 at 12:45
  • 1
    In additino to @MikeSeymour's wise note, with owning raw pointers there are problems also for _exception-safety_. – Mr.C64 Apr 16 '13 at 13:44