C++20 Modules: Reachability and Visibility
C++20's modules are one feature of the big four, supposed to influence how we write C++ code in a huge way. One expectation we discussed in (C++20 Modules: The possible speedup) is the improved compilation time. We also looked at encapsulation and controlling your interface in Controlling your interfaces.
In today's post, I like to talk about visibility and reachability.
Reachable but not visible
C++14 introduced a way to have an entity that is reachable but not visible, making it impossible to transform such code in C++ Insights correctly. I'm talking about
auto as a return type. The difference to C++11, where we already could declare
auto as a return type, is that in C++14, the trailing part is no longer required.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
If you look at this code with C++ Insights, you can see that
S. We can also change the value of the data member
x outside of
Fun. However, we cannot create an object of type
X outside of
Fun (at least not without
This is an example where we look at a case of an entity that is
reachable but not
visible. So far, we didn't need modules for this. I guess the case presented is not the most useful one.
Modules, reachability, and visibility
If we look back at last month's post, there we discussed the example of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
In this version, I did use
import <string> in the Purview of the module. By that, it follows the rules of named modules. Everything is private by default, except if we say
export. The implication is that, for example, in
main, we can import the module
Normalize and call
.size() on that result.
1 2 3 4 5 6 7 8
We can see that A doesn't compile. The type
std::string is not visible within
main. It is only visible inside the module
strcat. However, we can call
.size() on the return type of
Normalize because this function is reachable. We are looking at the same case as in the C++14 example.
Whether this is good or bad is a very delicate question. It seems totally weird that we have a type that we cannot really use. On the other hand, not polluting everybody's translation unit comes with a certain interesting aspect.
Anyhow, we can change the behavior by re-exporting the imported module prefixing it with
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
This means that we are back at the topic of last month's post. We have much finer control over our interfaces. It also means that we have to consider the fact above, and whenever we
import something, we need to ask the question of whether it makes sense to
export it as well, what a great new world modules bring us!