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 Fun
returns 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 decltype
).
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 Normalize
.
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 strcat
, call 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 export
:
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!
Andreas