I rarely use these features. Part of the reason for this chapter is for those rare circumstances in which they are useful. Another is understanding why they’re less popular. And because it’s fun to look ahead, there are sneak previews of two C++20 features that aren’t so useful yet, but should be in C++23: modules and coroutines.
protected sections, protected inheritance
But now we’ve reached the dawn of human civilization and there are MobilePhones. They make calls differently, using cell towers, but they need to increment that number too. They can’t access Phone::incNumCalls(); it’s private. And we decided for good reason not to make it public. What else can we do?
The Phone class, ready to share a family secret with its child classes

Public inheritance with a protected section

Private inheritance with a protected section
Problem: SatellitePhone can’t use incNumCalls. Private inheritance put it in MobilePhone’s private section.

protected inheritance: a solution to SatellitePhone’s problem
Verifying that no matter what kind of call we made, Phone::numCalls_ got updated
It doesn’t at all matter whether you use private or protected inheritance until you have a grandchild class. Even then it probably won’t matter. I almost never need protected sections or protected inheritance.
friends and why you shouldn’t have any

A four-Area map, with a bounding box shown on one Area
area.h
The map program, which identifies the Area furthest north
I know I’ve written clear, well-commented code here (and I’m humble too), so I won’t explain further. But when furtherNorthThan tries to access boundingBox_, the compiler complains of a privacy violation. It’s right: boundingBox_ is private.
A friend for Area
Now the program should compile fine and report that Morgravia is furthest north.
Is this a good idea?
According to Marshall Cline and the C++ Super-FAQ,2 yes. He argues that a friend function is part of the public interface just as a public member function is. It doesn’t violate security, but is just another part of it.
I see his point, but I can’t think of an example that can’t be done another way. In this example, we could replace bool furtherNorthThan (const Area& a, const Area& b); with bool Area::furtherNorthThan (const Area& b) const; . That’s what we do with operators like <. Why not this too?
I used to make stream I/O operators >> and << friends of the classes they printed/read; now I have them call member functions print and read. Using friend might be easier, but not by much.
If you want it, use it as experts suggest: for things tightly connected to the class in question, so they can be considered part of the class’s interface to the world. I’m betting it won’t be often.
User-defined conversions (cast operators)
Should we add a way to implicitly cast from String to const char* as needed? Makes sense. Many built-in functions expect a char*, and you might prefer myInFile.open (filename); to myInFile.open (filename.c_str()), especially around the 100th time you type it. So we’ll add this operator to String:operator const char* () const { return c_str (); } // called implicitly as needed.
It no longer compiles – gives complaints about ambiguity or too many overloads.
It’s right. There are now two ways to match the arguments of operator ==: implicitly convert "END" to another String and compare with String’s ==; and implicitly convert str1 to a char* with the cast operator and use char*’s ==.
Giving String a user-defined cast operator
It works, but did we gain anything over saying filename.c_str()?
I never seem to find a way to use this feature that is both safe and time-saving. Maybe you will.
- 1.
Add a cast-to-double operator to the Fraction class. The double version of 1/2, for example, is 0.5 (of course).
- 2.
Add a cast-to-double operator to the Point2D class from earlier exercises. The double version of a Point2D is the magnitude:
.
Modules
Programmers who aren’t me get concerned about the time it takes to load those increasingly long .h files. There are also issues we’ve skirted: a #define you include in one .h file may interfere with another. We try to avoid that by conventions for naming those defines; we may fail and get horrific error messages.
Modules are a fix. A module can be compiled once, rather than recompiled for every .cpp file that uses it, unlike a .h file;4 that should shorten compile times. It can also specify what its writer wants shared with the world, preventing some name conflicts. (A .h file makes everything visible to its includer, and a .cpp file shows nothing, but a module gets to choose.) And a module can all be in one file if you want – you don’t have to split between .cpp and .h files.
I’m sure it’ll work as planned. But the standard itself isn’t complete: one of the “top priorities” for the upcoming C++23 standard is to put the standard library in modules, which means they haven’t done it yet.5 I’ll wait, and I recommend you do as well.
But of course I can’t let it go at that – so here’s an online extra.
See github.com/Apress/cpp20-for-lazy-programmers for an up-to-date walk-through of how to use modules as best compilers now support them.
Coroutines
Ordinarily a function, if you call it a second time, starts at the beginning again. A coroutine can pick up right where it left off.
A program using a coroutine in Microsoft Visual Studio. g++ and the C++20 standard aren’t equipped for this yet
To trace its action: the first time it’s called, it sets whichOne – which factorial we’re to return – to 0. The result for 0 is 1. (You might load the source code example in the ch26 folder and trace it in the debugger. That’s what I did.)
It enters the loop. First thing it’s to do is provide the caller main with that result, with co_yield, which means “give result to the caller, and when called again, resume execution here.” So control returns to main, which prints that result.
When main calls it again, it continues from where it stopped: at the co_yield. It goes on and adds 1 to whichOne (changing whichOne to 1), multiplies result by whichOne (getting 1 again), goes to the next iteration of the loop, and co_yields that result.
When called again, it’ll increment whichOne again (getting 2), multiply result by whichOne (getting 2), and co_yield that result.
The next time, whichOne will become 3 and result will be 6. And so on.
main is set up to call this again and again in a range-based for loop, breaking at 8 (gotta stop somewhere).
One advantage of coroutines is efficiency. Each time we call factorial, all it does is an increment, a multiplication, and a return. It’s O(1)! The version in Chapter 18 was O(N). Programmers also report that for some problems, coroutines are more intuitive and easier to write.
The big disadvantage for now is support. As you saw, Visual Studio considers its generator template experimental, and g++ doesn’t have it at all. Both support coroutines – both have co_await, co_result, and co_yield – but generator isn’t standard, and I think it’s best to write code that’ll work on any machine. In g++ you’d have to write it yourself, and it’s not easy. Same for other things you may want to do. My hope – and there’s chatter in the community about this7 – is that C++23 will remedy this problem.
- 1.
Adapt Example 27-7 so that factorial returns not just result, but result with whichOne in a structured binding – so main won’t have to keep track of its own whichOne independently. This isn’t really practice with coroutines but with structured bindings, but still worth doing, I think.
- 2.
Write another generator function that returns the next prime number, starting with 2, and a version of main that prints the first 100 primes.