The template and typename keywords in C++
There are two uses for the template
and typename
keywords in C++. One of them is fairly well known amongst programmers: to define templates. The other use is more obscure: to specify that an expression refers to a template function or a type. This regularly trips up programmers that use the Eigen library, often leading to error messages from the compiler that are difficult to understand, such as "expected expression" or "no match for operator<".
Using the template and typename keywords to define templates
The template
and typename
keywords are routinely used to define templates. This is not the topic of this page as we assume that the reader is aware of this (otherwise consult a C++ book). The following example should illustrate this use of the template
keyword.
template <typename T> bool isPositive(T x) { return x > 0; }
We could just as well have written template <class T>
; the keywords typename
and class
have the same meaning in this context.
An example showing the second use of the template keyword
Let us illustrate the second use of the template
keyword with an example. Suppose we want to write a function which copies all entries in the upper triangular part of a matrix into another matrix, while keeping the lower triangular part unchanged. A straightforward implementation would be as follows:
Example: | Output: |
---|---|
#include <Eigen/Dense> #include <iostream> using namespace Eigen; void copyUpperTriangularPart(MatrixXf& dst, const MatrixXf& src) { dst.triangularView<Upper>() = src.triangularView<Upper>(); } int main() { MatrixXf m1 = MatrixXf::Ones(4,4); MatrixXf m2 = MatrixXf::Random(4,4); std::cout << "m2 before copy:" << std::endl; std::cout << m2 << std::endl << std::endl; copyUpperTriangularPart(m2, m1); std::cout << "m2 after copy:" << std::endl; std::cout << m2 << std::endl << std::endl; } | m2 before copy: 0.68 0.823 -0.444 -0.27 -0.211 -0.605 0.108 0.0268 0.566 -0.33 -0.0452 0.904 0.597 0.536 0.258 0.832 m2 after copy: 1 1 1 1 -0.211 1 1 1 0.566 -0.33 1 1 0.597 0.536 0.258 1 |
That works fine, but it is not very flexible. First, it only works with dynamic-size matrices of single-precision floats; the function copyUpperTriangularPart()
does not accept static-size matrices or matrices with double-precision numbers. Second, if you use an expression such as mat.topLeftCorner(3,3)
as the parameter src
, then this is copied into a temporary variable of type MatrixXf; this copy can be avoided.
As explained in Writing Functions Taking Eigen Types as Parameters, both issues can be resolved by making copyUpperTriangularPart()
accept any object of type MatrixBase. This leads to the following code:
Example: | Output: |
---|---|
#include <Eigen/Dense> #include <iostream> using namespace Eigen; template <typename Derived1, typename Derived2> void copyUpperTriangularPart(MatrixBase<Derived1>& dst, const MatrixBase<Derived2>& src) { /* Note the 'template' keywords in the following line! */ dst.template triangularView<Upper>() = src.template triangularView<Upper>(); } int main() { MatrixXi m1 = MatrixXi::Ones(5,5); MatrixXi m2 = MatrixXi::Random(4,4); std::cout << "m2 before copy:" << std::endl; std::cout << m2 << std::endl << std::endl; copyUpperTriangularPart(m2, m1.topLeftCorner(4,4)); std::cout << "m2 after copy:" << std::endl; std::cout << m2 << std::endl << std::endl; } | m2 before copy: 7 9 -5 -3 -2 -6 1 0 6 -3 0 9 6 6 3 9 m2 after copy: 1 1 1 1 -2 1 1 1 6 -3 1 1 6 6 3 1 |
The one line in the body of the function copyUpperTriangularPart()
shows the second, more obscure use of the template
keyword in C++. Even though it may look strange, the template
keywords are necessary according to the standard. Without it, the compiler may reject the code with an error message like "no match
for operator<".
Explanation
The reason that the template
keyword is necessary in the last example has to do with the rules for how templates are supposed to be compiled in C++. The compiler has to check the code for correct syntax at the point where the template is defined, without knowing the actual value of the template arguments (Derived1
and Derived2
in the example). That means that the compiler cannot know that dst.triangularView
is a member template and that the following < symbol is part of the delimiter for the template parameter. Another possibility would be that dst.triangularView
is a member variable with the < symbol referring to the operator<()
function. In fact, the compiler should choose the second possibility, according to the standard. If dst.triangularView
is a member template (as in our case), the programmer should specify this explicitly with the template
keyword and write dst.template triangularView
.
The precise rules are rather complicated, but ignoring some subtleties we can summarize them as follows:
- A dependent name is name that depends (directly or indirectly) on a template parameter. In the example,
dst
is a dependent name because it is of typeMatrixBase<Derived1>
which depends on the template parameterDerived1
. - If the code contains either one of the constructs
xxx.yyy
orxxx->yyy
andxxx
is a dependent name andyyy
refers to a member template, then thetemplate
keyword must be used beforeyyy
, leading toxxx.template yyy
orxxx->template yyy
. - If the code contains the construct
xxx::yyy
andxxx
is a dependent name andyyy
refers to a member typedef, then thetypename
keyword must be used before the whole construct, leading totypename xxx::yyy
.
As an example where the typename
keyword is required, consider the following code in Sparse matrix manipulations for iterating over the non-zero entries of a sparse matrix type:
SparseMatrixType mat(rows,cols); for (int k=0; k<mat.outerSize(); ++k) for (SparseMatrixType::InnerIterator it(mat,k); it; ++it) { /* ... */ }
If SparseMatrixType
depends on a template parameter, then the typename
keyword is required:
template <typename T> void iterateOverSparseMatrix(const SparseMatrix<T>& mat; { for (int k=0; k<m1.outerSize(); ++k) for (typename SparseMatrix<T>::InnerIterator it(mat,k); it; ++it) { /* ... */ } }
Resources for further reading
For more information and a fuller explanation of this topic, the reader may consult the following sources:
- The book "C++ Template Metaprogramming" by David Abrahams and Aleksey Gurtovoy contains a very good explanation in Appendix B ("The typename and template Keywords") which formed the basis for this page.
- http:/
/ pages.cs.wisc.edu/ ~driscoll/ typename.html - http:/
/ www.parashift.com/ c++-faq-lite/ templates.html#faq-35.18 - http:/
/ www.comeaucomputing.com/ techtalk/ templates/ #templateprefix - http:/
/ www.comeaucomputing.com/ techtalk/ templates/ #typename