Why export templates would be useful in C++

Export templates

The C++ standard defines the concept of "export templates". These are template classes and functions which are only declared wherever they are used (ie. usually in a header file), while implemented only once in some compilation unit.

In other words, you could have a header file like:

// the_header_file.hh
export template<typename T>
void foo(const T& value);

And then you could use it somewhere like:

#include "the_header_file.hh"

int main()
{
    foo(123);
    foo("456");
}

Note how the calling code inside main() doesn't see how the foo() template function has been implemented, like it's usual with regular templates. It only sees a declaration of it.

The implementation of foo() can then be put in some source file to be compiled separately.

What this means in practice is that the linker will have to launch the compiler in order to compile the instantiations of foo() for each type it's used with.

Misconceptions about misconceptions about export templates

No, that's not a typo in the heading.

Whenever you see discussions about export templates, you will invariably see both some misconceptions about them and also misconceptions about these misconceptions themselves, especially in the cases when someone is arguing why export templates are actually unneeded and useless overhead in the standard (and thus should be removed from there).

Invariably people (even expert C++ programmers and designers) will list the wrong reasons why export templates are "useless" and should be removed from the standard. They will typically list misconceptions people have about export templates such as:

Almost invariably, whether these arguments are actually true or not is a matter of argument (and would also depend on the actual implementation of export templates in the compiler). The people advocating the removal of export templates from the C++ standard claim that all these arguments are misconceptions and thus the export template feature is more or less useless.

Both types of people (those who may have misconceptions about export templates and those who argue against those misconceptions and promote removing export templates from the standard) are completely missing the absolutely most important point with export templates.

Export templates increase modularity

The absolutely most important feature of export templates is that they increase modularity and locality of templated code. This is something I have never seen discussed by these people.

This is something that should definitely not be taken lightly.

As an example of how export templates increase modularity, consider the following piece of code in some compilation unit:

namespace
{
    class SomeClass { /* ... */ };

    std::map<SomeClass, AnotherType> someLocalMap;

    void performSomeLocalOperation() { /* some code here */ }
}

export template<typename T>
void foo(const T& value)
{
    // some code that utilizes someLocalMap
    // and performSomeLocalOperation()
}

Note that in the above example SomeClass, someLocalMap and performSomeLocalOperation() are local to the compilation unit they reside in, and hence are not visible nor accessible from anywhere else.

Also note that the foo() template function does have access to them, by the simple fact that its implementation is located in the same compilation unit.

This is something that is simply not possible with regular non-export templates. Non-export templates are instantiated wherever they are used, and hence they do not have access to the local data of a different compilation unit (there isn't even be any syntax for them to even try).

This is a very powerful modularity feature that should definitely not be taken lightly. The lack of export templates makes template functions and classes inferior to regular functions and classes in this regard: They cannot have data that is local to their own compilation unit.

(Template classes can have some kind of "local" data in the form of static members. However, this is not the same thing: Each distinct template instantation of this class will have its own separate versions of these static members, distinct from the others. They will not be shared by all the template instantiations, as data in a local compilation would be. This can in some cases be a hindrance, if not even a complete showstopper.)

This is a very important point that most of these people are missing. This is why export templates would be useful and important.

"Manual" export templates

Incidentally, there's a way to "manually" simulate export templates in a program (although it has its limitations, of course), a fact which is surprisingly little known, even by expert C++ programmers. These "manual" export templates, if they can be used, can be quite useful and alleviate the modularity problems of regular templates in the same way as true export templates would.

It's a surprisingly little known fact that the implementation of a template function or class is actually not mandatory alongside its declaration. You can, in fact, write code like this:

// main.cc
template<typename T>
void foo(const T& value);

int main()
{
    foo(123);
}

This will not cause a compiler error. It's not mandatory for the foo() implementation to be provided alongside its declaration. What the above code does is, effectively, to "instantiate a declaration" of a foo() function taking a const int reference as parameter.

Naturally if the above is all your code, what you will get is a linker error because the linker cannot find an implementation of foo().

If you implement the foo() template function somewhere and instantiate it with the type int, then the program will actually link and work ok.

You can take advantage of this in order to implement "manual" export templates. In other words, templates which work almost exactly like export templates, with the only relevant difference being that you will have to instantiate the template manually for each type rather than the compiler doing it for you.

In the above example you could have another source file which has, for example, the following code:

// foo.cc
#include <iostream>

template<typename T>
void foo(const T& value)
{
    std::cout << value << std::endl;
}

template void foo(const int&); // Explicit instantiation

When you compile and link both of the above source files, the compilation will succeed and the program will work as expected.

As you can see, the implementation of foo() appears only inside the foo.cc source file and is not seen in any way or form by the code inside the main.cc file, yet it works. This is almost exactly how an export template would work.

Most importantly, foo.cc could have local data inside a nameless namespace, and the foo() function would be able to access it, exactly like an export template would.

The exact same trick can be used with template classes. In this case it would be the class which requires an explicit instantiation for each used type (using the syntax "template class TheClass<int>;").

Of course the limitation here is that every time you use the template function or class with a new type, you'll have to add a new explicit instatiation for that type in the source file where the template is implemented.

What export templates would do is to avoid having to do that: The compiler would, effectively, automatically instantiate the template for each type so you don't have to do it yourself manually.

In conclusion

Template functions and classes are inferior to regular functions and classes because templates cannot have data that is local to their own compilation unit. (Template classes can have data that is local to the class itself, but this data will not be shared by different template instantiations of that class.) This decreases the modularity of templates.

Export templates would solve this problem. Explicit instantiation of templates ("manual export templates") solves the problem partially, but it's an inconvenient and limited tehcnique.

The C++ standard will most probably deprecate or obsolete export templates in the future (maybe even with the next version of the standard). This decision is driven simply by the fact that compilers are not implementing the feature, not because there would be something inherently wrong or flawed about export templates. In other words, compiler vendors and developers are dictating the standard ("it's too laborious to implement") instead of it being dictated by what would be the best for the programmers.

That's a shame.

Many people argue that export templates are not useful at all, and that all the potential advantages are just misconceptions, and thus there are no such advantages, and hence it's just a good thing that they are removed from the standard. This is a lie. Export templates do have significant advantages in terms of modularity (and, in fact, there are things which can be done with regular functions and classes that simply cannot be done with templated versions), and the only reason they will be removed from the standard is because compiler vendors deem it as "too laborious to implement", not because there would be something inherently wrong with them from the programmer's point of view.