Tag Archives: Templates

Template specialisation with a non-type template parameter

The problem

Say you have a generic matrix class like this with non-type template parameters for the number of rows and columns:

template <typename ElementType, size_t Rows, size_t Columns>
class Matrix
{
    ElementType entries_[Rows][Columns];
    // etc...
};

You would like to create a specialization for a column vector, where the number of columns is always 1. However, the following typedef doesn’t compile:

typedef Matrix<size_t, Rows, 1> ColumnVector;

C++03

In C++03, you have 2 options.
Firstly, you can use inheritance:

template <typename ElementType, size_t Rows>
class ColumnVector : public Matrix<ElementType, Rows, 1>
{
};

Secondly, you could use a class with a nested typedef:

template <typename ElementType, size_t Rows>
struct ColumnVector
{
    typedef Matrix<ElementType, Rows, 1> type;
};

In this case, you could declare a column vector with:

ColumnVector<int, 3>::type vec;

C++11

In C++11, you can use an alias declaration to solve the problem more neatly:

template <typename ElementType, size_t Rows>
using ColumnVector = Matrix<ElementType, Rows, 1>;

This is a good example of how much more powerful the new alias declarations with using are than typedefs.

Finding the size of arrays and containers

Arrays

You can find the size of an array with the following template function:

template <typename T, size_t N>
size_t size(T (&)[N])
{
    return N;
}

Example:

#include <iostream>

int main()
{
    int ints[] = {1, 2, 3, 4};
    const char *pchars[] = {"a", "b", "c", "d"};
 
    std::cout << size(ints) << "\n";
    std::cout << size(pchars) << "\n";
}
4
4

Note that once an array has decayed to a pointer by being passed to a function, this will not work.

Containers

You can extend this to containers with the following two functions, which take template template parameters:

template <template<class, class> class Container, class T, class Allocator>
size_t size(const Container<T, Allocator>& cont)
{
    return cont.size();
}

template <template<class, class, class, class> class Container, class Key, class T, class Compare,
        class Allocator>
size_t size(const Container<Key, T, Compare, Allocator>& cont)
{
    return cont.size();
}

Example:

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>

int main()
{
    std::vector<int> vec(10);
    std::iota(vec.begin(), vec.end(), 0);
    std::map<std::string, int> dict;
    dict["apples"] = 7;
    dict["pears"] = 9;

    std::cout << size(vec) << "\n";
    std::cout << size(dict) << "\n";
}
10
2

C++11

In C++11, you can use decltype to select the container overload, so the two template template functions can be replaced with this:

template <class Container> 
auto size(const Container& cont) -> decltype(cont.size())
{
    return cont.size();
}

C++17

C++17 has added the std::size() function, which probably uses the size_t overload for arrays, and the one using decltype for containers.

Template instantiations in a CPP file

You can put explicit template instantiations in a separate CPP file and it will compile and link successfully as long as you instantiate all of the types you’re going to use. Note that all you need to do to instantiate a template is to declare it with the explicit types:

Header file:

class C
{
public:
    template <typename T>
    T method(const T& t1, const T& t2);
};

CPP file:

#include <string>

#include <instantiation.h>

template <typename T>
T C::method(const T& t1, const T& t2)
{
    return t1 + t2;
}

// These are the explicit instantiations
template int C::method<int>(const int&, const int&);
template std::string C::method<std::string>(const std::string&, const std::string&);

Program file

#include <iostream>
#include <string>

#include <instantiation.h>

int main()
{
    C c;
    std::string str1 = "Hello ";
    std::string str2 = "World!";
    std::cout << c.method(str1, str2) << "\n";
    std::cout << c.method(1, 2) << "\n";
}
Hello World!
3

The inline keyword in C++

You shouldn’t use inline as a hint to the compiler that your code should be inlined – the compiler knows much better than you what to inline. My rules for using inline are:

You must use inline if you are defining an explicit specialization or instantiation of a template in a header
You should use inline if you are defining a function in a header that will be compiled into multiple compilation units

Class or typename for template parameters

You can use class or typename in template parameters and they are equivalent, so the following two classes are exactly the same:

template <class T>
class C1
{
    T t_;
};
template <typename T>
class C1
{
    T t_;
};

As a matter of personal choice, I tend to use class for a template parameter that can only be a class because the template refers to a member.

template <class T>
class C2
{
    T t_;
public:
    void f()
    {
        t_.f();
    }
};

And I use typename for a parameter that is not used in this way. I feel it is a useful hint as to what kind of parameter is expected.

The typename keyword

The typename keyword is used to force a dependent name to be treated as a type. For example, consider the following code:

template <class T>
class C1
{
    T::U* v_;
};

The default behaviour of the compiler is not to see T::U as a type, but instead to see it as the name of a variable, making the * symbol a syntax error. The addition of the typename keyword gives the compiler the necessary hint to treat it as a type.

template <class T>
class C1
{
    typename T::U* v_;
};

It can be necessary to add typename to function template return values and parameters for the same reason:

template <class T>
typename T::U F(const typename T::V& v)
{
}

Member typedefs can also require it:

template <class T>
class C2
{
    typedef typename T::U V;
};

And it may be needed in template argument lists:

template <class T>
class C3 : public C2<typename T::U>
{
};

An unrelated additional use of the typename keyword is that it may be used instead of the word class in a template parameter list. In this context it is entirely synonymous with the word class. It is just added to give a clearer indication that the template argument may be a primitive type as well as a class.

template <typename T>
class C4
{
};

Template implementations and header Files

When the compiler is instantiating a template class, it needs to have access to the implementation of the template class in order to generate the code. This means that you can’t put the declaration of a template class in a header file, and the implementation in a cpp file, and then just include the header file in client code. The linker won’t be able to find the implementation while it’s instantiating the template, and so will generate errors.

This is why template libraries like the STL and Boost are traditionally implemented as “header-only” libraries, with all of the implementation included inline in the header files. A related solution is to put the implementation code in a separate file (possibly with a “.tpp” extension) and then include that at the end of the header file. The disadvantage of these methods is that template instantiations cannot be shared between compilation units, and this causes code bloat.

You can keep the implementations separate by explicitly intantiating the templates for the types you want in the template class cpp file. This means that the implementation code is visible when the templates are instantiated, but this instantiation will only happen once, so code bloat is avoided. This is the approach taken by many compiled libraries. It means though, that only those explicit instantiations are available to clients.