Quick, what does this C++17 code print?

void foo(const std::istream&) { puts("istream"); } void foo(const std::ifstream&) { puts("ifstream"); } int main() { std::fstream t; foo(t); }

(Wandbox.) That’s right, it prints “istream” — the fstream class (input/output, file-based) is derived from istream (input, non-file) and also from ostream (output, non-file) but not from ifstream (input, file-based) and not from ofstream (output, file-based).

There is no inheritance relationship between fstream , ifstream , and ofstream . But there is an inheritance relationship between fstream and iostream . The spaghetti of C++98-era multiple inheritance looks like this:

Quick, what does this C++2a Working Draft code print?

void foo(Same<int> auto) { puts("exactly int"); } void foo(Integral auto) { puts("integral"); } void foo(Regular auto) { puts("regular"); } int main() { int t = 42; foo(t); }

Okay, trick question. This code doesn’t compile: out of Same<int> , Integral , and Regular , none of them subsumes any of the others, so the overload resolution is ambiguous. (This is not a bad thing, in this case! Nobody should be writing code like the above.)

The spaghetti of C++2a-era multiple subsumption looks like this:

Just like an old-time cartographer, I have filled in the blank space on the west side of my map with non-sequitur dragons; in this case RegularInvocable . (That’s right, there is no relationship between Regular and RegularInvocable — the C++2a Ranges proposal introduced the term Regular to the standard library and immediately overloaded it with multiple unrelated meanings.)

To be fair, I haven’t come up with an easy surprising example such as my ifstream example above. The best I can do is stuff like

Same<T, int> does not subsume Integral<T>

Same<T, int> does subsume Same<int, T> (!), and vice versa

DerivedFrom<T, Base> does not subsume Same<T, Base>

SwappableWith<T, T> does not subsume Swappable<T> , nor vice versa

Concepts are a lot weirder than classical inheritance, because they’re parameterized. As you can see above, Constructible<T> is a different concept (with different “derived concepts”) from Constructible<T, T> . And so we can get extra weirdnesses such as

DerivedFrom<T, std::ifstream> does not subsume DerivedFrom<T, std::istream>

So in conclusion: Remember the ifstream! Resist designing libraries with many couplings; resist implementing couplings on an ad-hoc basis. Today’s cutesy “because-we-can” spaghetti graph is tomorrow’s “but-why-would-you?” spaghetti graph.