inline meaning in module interfaces

This comes from P1779, just adopted in Prague a few days ago. From the proposal:

This paper proposes removing the implicit inline status from functions defined in a class definition attached to a (named) module. This allows classes to benefit from avoiding redundant declarations, maintaining the flexibility offered to module authors in declaring functions with or without inline. Moreover, it allows injected friends of class templates (which cannot be generically defined outside the class definition) to be non-inline at all. It also resolves NB comment US90.

The paper (among other things) removed the sentence:

A function defined within a class definition is an inline function.

and added the sentence:

In the global module, a function defined within a class definition is implicitly inline ([class.mfct], [class.friend]).


Your example with export module M would be the modular equivalent of the initial program. Note that compilers already do inline functions that aren't annotated inline, it's just that they additionally use the presence of the inline keyword in their heuristics.


Does that mean that, in a modules world, for the compiler to be able to optimize away function calls we have to annotate them as inline even if they are defined in-class?

To some degree.

Inlining is an "as if" optimization, and inlining can happen even between translation units if the compiler is clever enough.

That being said, inlining is easiest when working within a single translation unit. Thus, to promote easy inlining, an inline-declared function has to have its definition provided in any translation unit where it is used. This doesn't mean the compiler will certainly inline it (or certainly not inline any non-inline-qualified function), but it does make things a lot easier on the inlining process, since the inlining is happening within a TU rather than between them.

Class member definitions defined within a class, in a pre-module world, are declared inline implicitly. Why? Because the definition is within the class. In a pre-module world, class definitions that are shared among TUs are shared by textual inclusion. Members defined in a class would therefore be defined in the header shared among those TUs. So if multiple TUs use the same class, those multiple TUs are doing so by including the class definition and the definition of its members declared in the header.

That is, you're including the definition anyway, so why not make it inline?

Of course, this means that the definition of a function is now a part of the class's text. If you change the definition of a member declared in a header, this forces the recompilation of every file that includes that header, recursively. Even if the class's interface itself isn't changing, you still need to do a recompile. So making such functions implicitly inline doesn't change this, so you may as well do that.

To avoid this in a pre-module world, you can simply define the member in the C++ file, which won't get included in other files. You lose easy inlining, but you gain compile-time.

But here's the thing: this is an artifact of using textual inclusion as a means to deliver a class to multiple places.

In a modular world, you would probably want to define each member function within the class itself, as we see in other languages like Java, C#, Python, and the like. This keeps code locality reasonable, and it prevents having to re-type the same function signature, thus serving the needs of DRY.

But if all members are defined within the class definition, then under the old rules, all of those members would be inline. And in order for a module to allow a function to be inline, the binary module artifact would have to include the definition of those functions. Which means that any time you change even one line of code in such a function definition, the module will have to be built, along with every module depending on it, recursively.

Removing implicit-inline in modules gives users the same powers they had in the textual inclusion days, without having to move the definition out of the class. You can pick which function definitions are part of the module and which ones are not.