1

I have some template classes that are separated into header files and implementation files:

// MyTemplate.h
template<class T>
class MyTemplate {
public:
    void Foo();
};
// MyTemplate.cpp
#include "MyTemplate.h"
template<class T>
void MyTemplate<T>::Foo() {
    // ...
}

To use MyTemplate in another translation unit I can #include the implementation file:

#include "MyTemplate.cpp"
template class MyTemplate<int>;

This way I get to:

  1. Keep the interface and implementation in separate files.
  2. Not have to maintain a list of all template instantiations in the implementation file.

I am in the process of converting to C++ modules. Is there any way to achieve the same with modules, when I should no longer use #include?

Magnar Myrtveit
  • 2,432
  • 3
  • 30
  • 51
  • 7
    you shouldn't call it `.cpp` so it doesnt get confused with actual source files. `#include foo.cpp` is usually a no-go – 463035818_is_not_an_ai Apr 19 '23 at 14:13
  • 2
    A side note: You can achieve the same by putting the methods implementation in the header after the class definition. This is quite a common practice, whereas #including a cpp file is not. – wohlstad Apr 19 '23 at 14:13
  • 1
    Does this answer your question? [Why can templates only be implemented in the header file?](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Chris Apr 19 '23 at 14:18
  • 1
    It should be redundant to add `template class MyTemplate;` after including the implementation. `template class MyTemplate;` could be put **inside** `.cpp` file if the set of types `T` is known in advance. – Evg Apr 19 '23 at 14:22
  • 2
    The danger of `#include`ing cpp files is that, at some future time, someone - possibly you - may add the cpp files to the project file (makefile, etc). May not seem a problem now, but code has a way of lasting longer than you expect (say, into projects, at a time when you've forgotten that the cpp files were included). The effect will often be a project that compiles but will not link, due to multiple definitions. If you really want the definitions in a distinct file (and can't use modern capabilities, such as modules) use a different extension (e.g. .imp) and `#include` that. – Peter Apr 19 '23 at 14:23
  • @Peter if it would work as include file, I think it'd work without problem when compile, just like an empty file include it. (not say it's a good practice, but I fail to see how it'd break) – apple apple Apr 19 '23 at 14:39
  • @appleapple I didn't say it would fail on compiling. I said it can fail on linking. – Peter Apr 19 '23 at 15:01
  • @Peter yes I mean it would compile and link. – apple apple Apr 19 '23 at 15:03

1 Answers1

2

In modules, this is trivially done by simply creating a interface partition. This has to be an interface partition because the template definition is still part of your interface, so it has to be visible to those importing it (just like regular C++).

This is done as follows:

//Primary interface module
export module MyTemplates;

export import :TemplateDef;
//Other stuff for your module.
//Definition for your template.
export module MyTemplates:TemplateDef;

import :TemplateDecl;

template<class T>
export void MyTemplate<T>::Foo() {
    // ...
}
//Declaration for your template
export module MyTemplates:TemplateDecl;

template<class T>
export class MyTemplate {
public:
    void Foo();
};

That being said, because definitions have to be made available to everyone, there's little to be gained by separating these into two files. If you change the definition file, everyone importing your module still has to be recompiled. That's just how templates work.


Note that the non-modular form of your code is pretty bad. Separating template implementation from interface is fine; doing it with a file named ".cpp" is not. Headers are meant to be included; that's why we call them "headers". The assumption a user will make upon seeing a file with the ".cpp" extension is that it is not supposed to be included.

Just use the ".h" extension with both. ".hxx" is also often used for the implementation (which should be included by the ".h" version).

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Ah! That's clever :) Does that mean that the compiler will be able to inline functions implemented in `TemplateDef`, or will they have to be implemented in `TemplateDecl` for inlining to work? – Magnar Myrtveit Apr 20 '23 at 05:26
  • @MagnarMyrtveit Module partitions must be compiled before their primary module interface unit. The Primary module interface unit is compiled into a BMI, which is like an abstract syntax tree. Long story short: When compiling your "main" file, the compiler has access to the abstract syntax tree of the templated class, so yes - very much it is able to inline what it wants. But this probably depends, since different compilers implement modules differently. If you _really_ want inlining across translation units, the safest bet is probably to use LTO (link-time optimizations). – alexpanter Apr 21 '23 at 14:04
  • Or you may compile a simple example, and inspect the generated assembly with `-S` flag (depends on compiler), to see what is inlined or not. Depends on several factors, like how the compiler implements modules and what it does with them, what optimization flags you give it, and some inlining heuristics. I have not tried this, so can't say for sure what happens. – alexpanter Apr 21 '23 at 14:06