1

I have a class A with template argument T, which is limited to two types: T1 and T2. Because of this, I explicitly instantiated class A for types T1 and T2 such that A's functionality can be defined in a source file and doesn't need to be recompiled every time A.hpp is included.

A.hpp:

template<typename T>
class A {
public:
    void basicMethod();
};

template class A<T1>;
template class A<T2>;

A.cpp:

template<typename T>
void A<T>::basicMethod() {
    // ...
}

However, now I want to add a templated method to both A<T1> and A<T2> where the template argument is limited to two types again: S1 and S2:

A.hpp:

template<typename T>
class A {
public:
    void basicMethod();
    template<typename S>
    void advancedMethod();
};

template class A<T1>;
template class A<T2>;

// How to explicitly instantiate A::advancedMethod here?

A.cpp:

template<typename T>
void A<T>::basicMethod() {
    // ...
}

template<typename T>
template<typename S>
void A<T>::advancedMethod() {
    // ...
}

How do I explicitly instantiate A<T>::advancedMethod<S> for (T, S) = {T1, T2} x {S1, S2}? Based on what I found online, I tried adding things like this to the end of A.hpp:

template void A<T1>::advancedMethod(S1);
template void A<T1>::advancedMethod(S2);
template void A<T2>::advancedMethod(S1);
template void A<T2>::advancedMethod(S2);

However, this did not work at all.

Wout12345
  • 91
  • 5

1 Answers1

3

Explicit instantiation definitions for member function templates of class templates

You nearly got it. The correct syntax is:

template void A<T1>::advancedMethod<S1>();
template void A<T1>::advancedMethod<S2>();
template void A<T2>::advancedMethod<S1>();
template void A<T2>::advancedMethod<S2>();

Specifically noting that advancedMethod() is a function template parameterized over a single type template parameter, with no function parameters: your explicit instantiation definitions for different specialization of the function template (for different specializations of the class template in which it is defined) should, like the explicit instantiation definitions for the class template, provide the template arguments (<...>) as to specify the specializations you wish to explicitly instantiate.

Finally, the advancedMethod() function template naturally needs to be defined (either via a primary template definition or in an explicit specialization) for each specialization which is explicitly instantiated; particularly (cppreference):

If a function template, variable template, member function template, or member function or static data member of a class template is explicitly instantiated with an explicit instantiation definition, the template definition must be present in the same translation unit.


Separating definitions of template functions (/class template member functions) from their declarations: the -timl.hpp pattern

As an effect of the requirement above, when supplying explicit instantiation definitions, you typically place these after the definitions of the related templated entities, in a source file, where the typical use case is that a user of the public API of the templated entity should not have access to the definitions (as in your example). This is essential as you may not explicitly instantiate the same specialization twice over different translation units, as per As per [temp.spec]/5:

For a given set of template arguments,

  • an explicit instantiation definition shall appear at most once in a program,
  • [...]

An implementation is not required to diagnose a violation of this rule.

Thus, if you place the explicit instantiations in the header, and then include the header in more than one source file, your program will be ill-formed, NDR.

// a.hpp
template<typename T>
class A {
public:
    void basicMethod();
    template<typename S>
    void advancedMethod();
};

// a-timpl.hpp
#include "a.hpp"

template<typename T>
void A<T>::basicMethod() {
    // ...
}

template<typename T>
template<typename S>
void A<T>::advancedMethod() {
    // ...
}

// a.cpp
#include "a-timpl.hpp"
#include "t1.hpp"
#include "t2.hpp"
#include "s1.hpp"
#include "s2.hpp"

// Explicit instantiation definitions for
// production intent.
template class A<T1>;
template class A<T2>;

template void A<T1>::advancedMethod<S1>();
template void A<T1>::advancedMethod<S2>();
template void A<T2>::advancedMethod<S1>();
template void A<T2>::advancedMethod<S2>();

For test, you similarly include the -timpl.hpp header file rather than the main header (the main header is for the public API exposure), such that you may use the templated definitions to explicit instantiate specializations used in test:

// a_test.cpp
#include "a-timpl.hpp"
#include "t1_mock.h"
#include "t2_mock.h"
#include "s1_mock.h"
#include "s2_mock.h"

// Explicit instantiation definitions for
// test intent.
template class A<T1Mock>;
template class A<T2Mock>;

template void A<T1Mock>::advancedMethod<S1Mock>();
template void A<T1Mock>::advancedMethod<S2Mock>();
template void A<T2Mock>::advancedMethod<S1Mock>();
template void A<T2Mock>::advancedMethod<S2Mock>();
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • Thank you for your answer. Your syntax made a lot more sense, I just saw people in some other places putting the template parameters in between parentheses and didn't really understand why. I tried your syntax before and it didn't work, however it turns out that was because I put the explicit instantiations after the class definition/method declaration in A.hpp instead of after the method definition. Things now seem to compile and link, although I haven't actually tested calling the template method instances yet. – Wout12345 Nov 19 '20 at 15:59
  • As for the second part of your answer: I don't quite understand this, in what specific situations does this cause problems? Right now I'm putting the class instantiations right after the class definition (so in a header file) and the method instantiations right after their definitions (so in a source file). When I move the class instantiations to the end of one of the source files of that class, I get linker errors. – Wout12345 Nov 19 '20 at 16:10
  • @Wout12345 As per [\[temp.spec\]/5](https://timsong-cpp.github.io/cppwp/n4659/temp.spec#5), for a given set of template arguments, _"an explicit instantiation definition shall appear at most once in a program [...] An implementation is not required to diagnose a violation of this rule."_. If you place the explicit instantiations in the header, and then include the header in more than one source file, your program will be ill-formed, NDR (no diagnostic required). ... – dfrib Nov 19 '20 at 16:27
  • @Wout12345 ... If you get linker errors, you are doing something wrong: did you include the `a.hpp` file instead of the `a-timpl.hpp` file in the source file, perhaps? – dfrib Nov 19 '20 at 16:28
  • The header in question is include-guarded, so it can only appear once during my compilation process and the header is internal (not part of the public interface) so it won't be part of any other compilation processes. Does that mean it's never duplicated? – Wout12345 Nov 19 '20 at 19:36
  • @Wout12345 header guards protect against double inclusion of one header in _one single translation unit_. You can still include the header in separate translation units, in which case you will have an ODR-violation if you keep the explicit instantiation definitions in the header and _ever_ include the header in two different TU:s. I will not continue this chat, however, my answer is very clear on where you should place the explicit instantiation definitions to avoid ill-formed NDR. If you have more questions or do not understand this answer, consider asking a new question. – dfrib Nov 19 '20 at 19:44
  • Sure, sorry for the trouble. Thanks again for the answer. – Wout12345 Nov 20 '20 at 08:01