In the last article we talked about how to have multiple fundamental implementations for a single conceptual task. The notion being, if we can use type information to optimize a particular use case of our function, then why not do so and hide that benefit under our existing interface? Thus far we have limited our discussion primarily to functions, now we are going to turn our attention to structs and classes.
Having multiple implementations can be useful for optimization in the context of structs (just as we showed it was useful for functions in the last article). One example of this is
std::vector<bool>, which is space-optimized compared to other versions of
std::vector so that each boolean value it contains only actually occupies one bit of memory. We might also be interested in less dramatic variations in our struct, such as changing the deleter on a
For the most part, the code reuse techniques we've discussed already will transfer over nicely to structs and classes, so we'll spend most of our time talking about a new technique that offers more options with structs/classes.
Questions for next time:
1) What was the goal of overloading/tag dispatch? Why was it useful for implementing multiple fundamental implementations of a particular function?
2) Can we apply what we learned in the last article to provide multiple fundamental implementations of a struct? What are the limitations of overloading/tag dispatch when it comes to writing structs? You may find it helpful to revisit the first article of the series.
3) What technique allows us to similarly divide one concept, in this case, a type, into multiple implementations?
4) In what ways can you select implementations using the technique from question 3? For example, how might I apply the technique to single cases that must be handled specially (like
std::vector<bool> being different from all other implementations of
std::vector) vs. patterns of differences (as in how the implementation of
std::unique_ptr<T> varies from
5) What are the limitations of the scope of the technique from question 3? It may be useful to review your answer to question 1 for this. Overcoming this apparent limitation will be the subject of future blog article.
By this point in the series, you should be able to answer these fundamental code reuse questions:
1) How do I reuse the same implementation code with different types? (Review here)
2) How do I use the same code to invoke different fundamental implementations selected by type? (Review here)
3) How do I reuse code for some types but select different fundamental implementations for others? What if there are patterns to the "exceptions" to the "default code"? (Review here)
If you struggle with these concepts, I highly recommend going back and (re)reading the relevant articles, then playing around with the concepts in your own code until they start to click and make sense.