7

I'm creating a library and I have a class template inside a C++20 module and I want to add an instantiation in order to reduce compilation time for every project that uses my library.

Are these different implementations equivalent, or is there a better way to achieve it?

1)

//mod.cpp
export module mod;

export template<typename T>
struct mystruct{ T i;};

export template class mystruct<int>;
//mod.cpp
export module mod;

export template<typename T>
struct mystruct{ T i;};

template class mystruct<int>;
//mod.cpp
export module mod;

export template<typename T>
struct mystruct{ T i;};

export extern template class mystruct<int>;



//mod_impl.cpp
module mod;

template class mystruct<int>;

Edit: This answer says only that 2. works, but my point is if also 1. and 3. are equivalent to 2.

desio
  • 126
  • 6
  • Does this answer your question? [How to use template explicit instantiation with C++20 modules?](https://stackoverflow.com/questions/61520847/how-to-use-template-explicit-instantiation-with-c20-modules) – Osyotr Jan 09 '22 at 16:02
  • @Genjutsu that is exactly case 2., but I want to know if 1. and 3. bring to the same result or instead are different. – desio Jan 09 '22 at 16:05
  • 1
    @desio: So basically, you have two questions: 1) is `export`ing an explicit instantiation in the module interface important for exposing the explicit instantiation to users of the module? 2) if I `extern` it in the interface and instantiate it in the implementation, does that work just as well? – Nicol Bolas Jan 09 '22 at 16:05
  • @NicolBolas yes, and 3) if I don't export a template instantiation it'll be visible outside anyway or not? – desio Jan 09 '22 at 16:07
  • @desio: That's just a restatement of 1. – Nicol Bolas Jan 09 '22 at 16:08
  • Oh yes exactly! – desio Jan 09 '22 at 16:13

1 Answers1

1

Modules affect 2 things: the scope of names and the reach-ability of declarations. Both of these only matter if they are within the purview of a module (ie: in an imported module interface TU and not being in the global module fragment).

Names declared in the purview of a module can be used outside of that module only if they are exported by that module. In the case of an explicit template instantiation, the template itself is already exported, so users outside of the module can already use the name.

However, an explicit template instantiation definition is also a declaration. And modules control the reach-ability of declarations. The thing is, the rules of reach-ability for a declaration don't actually care about export:

A declaration D is reachable if, for any point P in the instantiation context ([module.context]),

  • D appears prior to P in the same translation unit, or
  • D is not discarded ([module.global.frag]), appears in a translation unit that is reachable from P, and does not appear within a private-module-fragment.

[ Note: Whether a declaration is exported has no bearing on whether it is reachable. — end note ]

Emphasis added.

These rules only care about which TUs have been imported (and whether the declaration is in a global/private module fragment).

Therefore, if the primary template declaration was exported, an explicit template instantiation (that is not in the global/private module fragment) in one of the imported module files is reach-able by any code that imports it.

So it doesn't matter if you export an explicit template instantiation. If the primary template was already exported, its name could already be used, so the only thing that matters is if the explicit template instantiation is visible.

So your #1 and 2 are functionally equivalent. And its best not to export something you don't need to.


As for the behavior of extern template, that's interesting.

While extern normalize denotes external linkage, this does not apply to extern template. So we don't have linkage problems. And since the extern template declaration is reach-able by importers of the module (as previously stated), they'll see it and respect it.

So the only question is whether the explicit definition in your "mod_impl.cpp" is also reach-able. Except that's not a question because only the declaration part of a definition is ever "reach-able". That is, reach-ability only matters for a declaration.

The explicit instantiation definition is in a different TU. Therefore, it will only be instantiated in that TU; code which imports the module reaches only the declaration. And therefore, it won't instantiate the template.

So yes, you can perform extern template gymnastics (though again, export doesn't matter). But it's no different than just putting the explicit instantiation in your module interface, and it's way cleaner to do that.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • For those who may have forgotten: [What is a C++ module purview?](https://stackoverflow.com/q/49204175/1593077) – einpoklum Jan 09 '22 at 18:56
  • @einpoklum: Your answer could use some updating to include the global and private module fragments, which are not part of a C++20's module purview. And also the `module;` construct that is required to start the global module fragment. – Nicol Bolas Jan 09 '22 at 18:57
  • Mentioned the global module fragment in footnote to the answer. But it doesn't matter all that much, I think. – einpoklum Jan 09 '22 at 20:12