14. Templates
14.1 Template Declarations
14.1.1 Use variadic templates rather than an ellipsis
Use of the ellipsis notation … to indicate an unspecified number of arguments should be avoided. Variadic templates offer a type-safe alternative.
#include <iostream> int print () { std::cout << std::endl; } // @@+ Compliant: variadic template function +@@ template void print (First const & v, Rest const & ... args) { std::cout << v; print (args ...); // recursive template instantiation or a call to print () } void foo (int32_t i) { print ("And only ", i, " little ducks came back."); }
If use of a variadic template is not possible, function overloading or function call chaining (e.g. similar to stream output) should be considered.
References
- HIC++ v3.3 - 11.6
- JSF AV C++ Rev C - 108
- MISRA C++:2008 - 8-4-1
14.2 Template instantiation and specialization
14.2.1 Declare template specializations in the same file as the primary template they specialize
Partial and explicit specializations of function and class templates should be declared with the primary template. This will ensure that implicit specializations will only occur when there is no explicit declaration available.
// file.h template<typename T> void foo (T & t) { ++t; } // file1.cpp #include "file.h" void bar1 () { int32_t i = 3; foo<int32_t> (i); // primary template used, i is now 4 } // file2.cpp #include "file.h" // @@- Non-Compliant -@@ template<> void foo<int32_t> (int32_t & t) { --t; } void bar2 () { int32_t i = 3; foo<int32_t> (i); // explicit specialization used, i is now 2 }
References
- JSF AV C++ Rev C – 104
- MISRA C++:2008 – 14-7-3
14.2.2 Do not explicitly specialize a function template that is overloaded with other templates
Overload resolution does not take into account explicit specializations of function templates. Only after overload resolution has chosen a function template will any explicit specializations be considered.
#include <cstdint> template<typename T> void f1 (T); // #1 template<typename T> void f1 (T*); // #2 template<> void f1<int32_t*> (int32_t*); // #3 // @@- Non-Compliant (Explicit specialization of #1) -@@ void f2 (int32_t * p) { f1(p); // Calls #2 f1<int32_t*>(p); // Calls #3 }
References
- MISRA C++:2008 – 14-8-1
14.2.3 Declare extern an explicitly instantiated template
Declaring the template with extern will disable implicit instantiation of the template when it is used in other translation units, saving time and reducing compile time dependencies.
#include <cstdint> // t.h template <typename T> class A1 { }; template <typename T> class A2 { }; extern template class A2<int32_t>; // t.cpp #include "t.h" template class A1<int32_t>; // @@- Non-Compliant -@@ template class A2<int32_t>; // @@+ Compliant +@@