I work on a large software product which, being over 20 years old, has a number of constructs which have been superseded by language updates.
One of these is a home-rolled smart pointer template class which acts similarly to – but is not quite indentical to –
std::unique_ptr. The class has its destructor specialised for a number of types with specific destruction needs, and is referenced of the order of 150-200 times. It apparently works perfectly.
Were the application to be written from scratch now, I’m reasonably certain that
std::unique_ptr would be used instead. How aggressively should we pursue replacing the home-rolled class with
std::unique_ptr; possibly including
typedefs for the common specialisations with specific destructors?
This is a common problem for maintainers of old/legacy code, and there is no single perfect answer. Before making the decision to replace working code, I would propose trying to answer these three questions.
Is it possible? Can you devise a method whereby existing instances of the ‘old way’ can be reliably replaced by the ‘new way’ such that each change is guaranteed to be made correctly, to be tested and to pass all tests, and that no new bugs will be introduced?
Is there a benefit? Can you identify any aspect of the code that will be better after making this change? Will the code be faster/smaller/more maintainable as a result of the change? Will making the change uncover bugs you didn’t know about? Is the benefit large enough?
Is it enabling? Is this code under active development and there is some other planned set of changes that becomes possible/easier/safer as a result of first making this change (which would offset the risk of introducing bugs and the lack of a direct benefit)? Or is this a mature, stable code base that really needs to be protected against tampering by enthusiastic newcomers?
This last is often the decider. If there are big changes ahead for this code then getting rid of obstacles like home-brew templates might well be justified, where otherwise it is usually best to let sleeping dogs lie.
The main advantage of doing this replacement would be the cost of bringing in new people.
If you bring in a new C++ programmer, they will have to learn the particulars and vagaries of your smart pointer. Whereas if you used
unique_ptr, there is a reasonable chance that they already know its peculiarities. And if they don’t, then there’s an entire Internet of information about it. That’s one of the things that makes vocabulary types good: everyone already knows how they work, and if not, information is readily available.
You mention that your smart pointer has special code structures that are designed to interact with specific other classes. That’s represents non-trivial domain knowledge that users of that smart pointer need to know. Such domain knowledge must not only be acquired by new programmers, this knowledge will for the most part be useless outside of your project.
Whereas time spent understanding
unique_ptr will be valuable over one’s C++ career.
That’s not to say that you necessarily should make the change. But that would be the primary advantage in doing so.
This is a problem for many legacy systems. On one hand, this custom
std::unique_ptr-like capability has been around for a long time. Bugs were wrung out long ago, and people on the project know how to use it. “It ain’t broke, so don’t fix it”.
On the other hand, it is broke in a sense. One of the biggest challenges facing older solutions to some problem is competition that has emulated what you have done but have done so using modern techniques. That competition will surpass you if you don’t continuously improve your processes and your product. This custom
std::unique_ptr-like capability is most likely one of many techniques in your twenty year old system that, while innovative in their time, are now archaic, cumbersome, and are slowing down your development process.