When would you use template explicit instantiation

2019-01-24 00:49发布

问题:

I've just been reading about template explicit instantiation:

template struct MyStruct<long>;

It was described as "quite rare", so under what circumstances would it be useful?

回答1:

One of the use cases is to hide definitions from the end-user.

tpl.h:

template<typename T>
void func(); // Declaration

tpl.cpp:

template<typename T>
void func()
{
    // Definition
}

template void func<int>(); // explicit instantiation for int
template void func<double>();  // explicit instantiation for double

main.cpp

#include "tpl.h"
int main()
{
    func<double>(); // OK
    func<int>(); // OK
    // func<char>(); - Linking ERROR
}


回答2:

Explicit instantiation is designed to optimize template libraries usage providing some of (mostly used) template instances in compiled binary form instead of source code form. This will reduce compile and link time for end-user applications. E.g. std::basic_string<char> and std::basic_string<wchar_t> can be explicitly instantiated in STL distribution avoid work on its instantiation in each translation unit.

Explicit instantiation is also useful when you want to encapsulate template implementation and you want this template to be used only with well-known set of types. In this case you can place only declarations of template functions (free or members) in header file (.h/.hpp) and define them in translation unit (.cpp).

Example:

 // numeric_vector.h
 //////////////////////////////////////////////////
 template <typename T> class numeric_vector
 {
    ...
    void sort();
 };


 // numeric_vector.cpp
 //////////////////////////////////////////////////
 // We know that it shall be used with doubles and ints only,
 // so we explicitly instantiate it for doubles and ints
 template class numeric_vector<int>;
 template class numeric_vector<double>;

 // Note that you could instantiate only specific
 // members you need (functions and static data), not entire class:
 template void numeric_vector<float>::sort();

 template <typename T> void numeric_vector<T>::sort()
 {
   // Implementation
   ...
 }

Also explicit instantiation can be useful when you need instantiated type from template but inside some syntax construction that doesn't trigger instantiation itself, e.g. some compiler-specific meta-feature like __declspec(uuid) in Visual Studio.

Note the difference with another technique that could be used for implementation encapsulation - explicit specialization. With explicit specialization you must provide specific definition for each type to be specialized. With explicit instantiation you have single template definition.

Consider the same example with explicit specialization:

Example:

 // numeric_vector.h
 //////////////////////////////////////////////////
 template <typename T> class numeric_vector
 {
    ...
    void sort();
 };

 template <> class numeric_vector<int>
 {
    ...
    void sort();
 };

 template <> class numeric_vector<double>
 {
    ...
    void sort();
 };

 // Specializing separate members is also allowed
 template <> void numeric_vector<float>::sort();

 // numeric_vector.cpp    
 //////////////////////////////////////////////////
 void numeric_vector<int>::sort()
 {
   // Implementation for int
   ...
 }

 void numeric_vector<double>::sort()
 {
   // Implementation for double
   ...
 }

 void numeric_vector<float>::sort()
 {
   // Implementation for float       
   ...
 }


回答3:

Having an explicit specialization allows you to hide the implementation, which, as you know, is usually impossible with templates.

I've seen this technique only once in a library that handled geometry, and they'd provide their own vector class.

So you could use

lib::Vector<MyShape>

with some basic functionality that lib::Vector provided, and basic implementations, and if you used it with their classes (some, not all)

lib::Vector<lib::Polygon>

you would use the explicit specialization. You wouldn't have access to the implementation, but I'm betting some hardcore optimizations were going on behind the scenes there.



回答4:

If you really don't like defining template functions in header files, you can define the functions in a separate source file and use explicit template instantiation to instantiate all the versions you use. Then you only need a forward declarations in your header file instead of the complete definition.