I dug up an old Grid class, which is just a simple 2-D container templated with a type. To make one you would do this:
Grid<SomeType> myGrid (QSize (width, height));
I tried to make it “Qt-ish”…for instance it does size operations in terms of QSize, and you index into it with myGrid[QPoint (x, y)]. It can take boolean masks and do operations on elements whose mask bit was set. There’s also a specialization where if your elements are QColor it can generate a QImage for you.
But one major Qt idiom I adopted was that it did implicit sharing under the hood. This turned out to be very useful in the QColor-based grids for the Thinker-Qt-based program I had.
However :-/ I also happened to have some cases where I’d written the likes of:
Grid< auto_ptr<SomeType> > myAutoPtrGrid (QSize (width, height));
When I moved up from auto_ptr to C++11’s unique_ptr, the compiler rightfully complained. Implicit sharing requires the ability to make an identical copy if needed…and auto_ptr had swept this bug under the rug by conflating copying with transfer-of-ownership. Non-copyable types and implicit sharing simply do not mix, and unique_ptr is kind enough to tell us.
(Note: It so happened that I hadn’t noticed the problem in practice, because the use cases for the auto_ptr were passing grids by reference…never by value. Still, this was bad code…and the proactive nature of C++11 is pointing out the potential problem before it happens.)
Ok, so…how might I design a generic container that can flip implicit sharing on and off? I really did want many of the Grid features when I was using the auto_ptr and it’s great if copying is disabled for non-copyable types…that catches errors! But having the implicit sharing work is nice as a default, when the type happens to be copyable.
Some ideas:
- I could make separate types (
NonCopyableGrid,CopyableGrid)…or (UniqueGrid,Grid) depending on your tastes… - I could pass a flag into the
Gridconstructor - I could use static factory methods (
Grid::newNonCopyable,Grid::newCopyable) but which would call the relevant constructor under the hood…maybe more descriptive - If possible, I might “detect” copyability on the contained type, and then either leverage a QSharedDataPointer in the implementation or not, depending?
Any good reasons to pick one of these methods over the others, or have people adopted something altogether better for this kind of situation?
If you were going to do it in a single container, I think the easiest way would be to use
std::is_copy_constructableto choose whether your data struct inherited fromQSharedData, and to replaceQSharedDataPointerwithstd::unique_ptr(QScopedPointerdoesn’t support move semantics)This is only a rough example of what I’m thinking as I don’t have Qt and C++11 available together: