8

On cppreference.com about modules it says about export:

Module interface units can export declarations and definitions, which can be imported by other translation units. [ … ]
All declarations and definitions exported in the module interface units of the given named module will be available in the translation unit using the import declaration.

Guessing from that, I'd export everything that users of the module want or need to use it. Everything not exported is an implementation detail of the module and none of the importers’ concern.

It also tells me that

a private module fragment [ … ] allows a module to be represented as a single translation unit without making all of the contents of the module reachable to importers.

What is the difference between putting something in the private module fragment than to just not exporting it? I guess there’s a difference between “available in the translation unit using the import declaration” and “reachable to importers”. But what is that difference from a practical point of view? As a guideline, when would I put something in the private module fragment and when just not exporting it?

(Highlight inside quotes by me.)

3 Answers 3

8

What is the difference between putting something in the private module fragment than to just not exporting it

The principle difference in terms of implementation is that non-exported definitions that are in a module interface can be imported by other pieces of code that are part of the same module (implementation units and other interface units of the module). They will have access to those declarations even though you don't export them. As such, those declarations must live (to some degree) in whatever file the compiler uses for that module.

By contrast, private module fragment code doesn't contribute to any accessible interface of the module. It acts like a module implementation unit.

But here's the thing: you wouldn't be able to tell the difference. Why? If the primary module interface has a private module fragment, there cannot be any other files that are part of the same module. That's expressly forbidden. The private module fragment as a tool exists to support cases where you want to ship a complete module as a single file. There's nothing that a private module fragment can do which a module implementation unit cannot.

So if there's a possibility of a private module fragment (ie: it's a single-file module), code that imports the module cannot see the unexported declarations.

The main purpose behind the private module fragment is that it is impossible to export things from within it. This keeps you from accidentally doing that in your single-file implementation.

3

The practical answer is making classes be incomplete in clients:

export module A;
struct X;
export X* make();

module :private;
struct X {};
X* make() {return new X;}

Omitting the module :private; line makes X complete in importers, because export affects name lookup, not declaration reachability.

1
  • 2
    Got it for classes. That means that the fact that the definition of make is in :private makes no difference. An importer can call it as the declaration is exported, and the definition isn't needed to call it anyway.
    – Bolpat
    Commented Mar 2, 2022 at 9:06
2

The answer is explained in more detail on cppreference

Quoting it here for completeness of the answer.

Private module fragment

Primary module interface unit can be suffixed by a private module fragment, which allows a module to be represented as a single translation unit without making all of the contents of the module reachable to importers.


module : private ;

declaration-seq(optional)

Private module fragment ends the portion of the module interface unit that can affect the behavior of other translation units. If a module unit contains a private module fragment, it will be the only module unit of its module.

export module foo;
export int f();
 
module :private; // ends the portion of the module interface unit that
                 // can affect the behavior of other translation units
                 // starts a private module fragment
 
int f() {        // definition not reachable from importers of foo
    return 42;
}

i.e. you can thus both export the declaration and hide the definition.

The use case is explained here

Thanks to a private module fragment, you can implement a module in one file and declare its last part as its implementation using module :private;. Consequently, a modification of the private module fragment does not cause recompilation.

4
  • 1
    "Consequently, a modification of the private module fragment does not cause recompilation." Recompilation typically happens when a file's modification date changes. How exactly does the build system know when you've only modified part of a file? I think this statement is rather dubious, especially considering the early state of module build system support. Commented Mar 1, 2022 at 14:40
  • Perhaps “does not necessitate recompilation” would be better. Commented Mar 1, 2022 at 16:58
  • @NicolBolas, I guess a sophisticated IDEs could notice that a file save instruction only affected lines inside the private module fragment and thus mark the file as “does not require recompilation of dependents”. A regular build system would require a copy of the module non-private part to compare to. Or at least a 256-bit hash of the non-private module part.
    – Bolpat
    Commented Dec 9, 2022 at 10:56
  • In the last code example, what difference does it make that f is defined in the private module fragment? The declaration is exported, so importers of foo can call f. For all that I understand, that the definition of f is in the private module fragment is totally irrelevant. It might be different for constexpr functions, but in this case, what is the difference?
    – Bolpat
    Commented Mar 25 at 12:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.