C++ template

For basic C++ template usage there are plenty of tutorials, so here will be shown features not used so often.

Template default parameters

Template parameter can have default type in the similar manner as function argument has default value. That is valid for template functions:

template<typename T, typename S = string>
S f(T t, S s)
{
    cout << "int f(T t, S s): t=" << t << ", s=" << s << endl;
    return s;
}

int main()
{
    string s = f(1, "Hello, World!");
    int i = f(0, 1);
    return 0;
}
                    
as well for template classes:
template<typename T, typename S = string>
class C
{
public:
    C(T t, S s) : _t(t), _s(s)
    {
        cout << "C(T t, S s): t=" << t << ", s=" << s << endl;
    }
    
private:
    
    T _t;
    S _s;
};

int main()
{
    C<int, int> c1(1, 2);
    C<int> c2(0, "Hello, World!");
    return 0;
}
                    
As shown, c2 is created by specifying only one type parameter.

Function overloading on template arguments

Overloading on template type parameters is valid:

template<typename T>
T f()
{
    cout << "T f(): ";
    return T();
}

template<typename T, typename S>
S f()
{
    cout << "S f(): ";
    return S();
}
                
If the compiler cannot resolve a call of instantiated function, then an error is reported.

Function overloading on return type

Return type is not part of signature of non-template function. So the code

int g()
{
    return 1;
}

float g()
{
    return 1.0;
}
                    
won't compile. However, a template function can be overloaded on return type:
template<typename T>
int f(T t)
{
    cout << "int f(T t): t=" << t << endl;
    return 1;
}


template<typename T>
bool f(T t)
{
    cout << "bool f(T t): t=" << t << endl;
    return true;
}

int main()
{
    int i =  static_cast<int(*)(const char*)>(f<const char*>)("Hello");
    bool b = static_cast<bool(*)(const char*)>(f<const char*>)("World");
    return 0;
}
                    
In the main() call to the function has to be casted, otherwise the compiler could not resolve it.

Function specialization

Specialization of template function can be tricky. Template like

template<typename T>
T mid(T a, T b, T c);
                    
is called base template. It can be specialized to a concrete double type like
template<>
double mid(double a, double b, double c);                    
                    
It can be also specialized with the type T*:
template<typename T>
T* mid(T* a, T* b, T* c);       
                    
Since there is no partial specialization on functions, T* mid(T* a, T* b, T* c) overloads T mid(T a, T b, T c). That characteristic will be used in the example below.

If one defines

template<>
const char* mid(const char* a, const char* b, const char* c);                    
                    
which one is specialized: T mid(T a, T b, T c) or T* mid(T* a, T* b, T* c)? It appears that it depends of the specializations order. For the case
template<typename T>
T mid(T a, T b, T c); // (1)

template<>
double mid(double a, double b, double c);                    

template<typename T>
T* mid(T* a, T* b, T* c); // (2)    

int mid(int a, int b, int c);

template<>
const char* mid(const char* a, const char* b, const char* c); // (3)
                    
the function (3) specializes (2), so mid("hello", "all", "world") calls (3). For the case
template<typename T>
T mid(T a, T b, T c); // (1)

template<>
double mid(double a, double b, double c);                    

template<>
const char* mid(const char* a, const char* b, const char* c); // (3)

template<typename T>
T* mid(T* a, T* b, T* c); // (2)    

int mid(int a, int b, int c);            
                    
when (3) goes before (2), (3) specializes (1), so mid("hello", "all", "world") calls (2).

Detailed explanation can be found on Why Not Specialize Function Templates. Shortly, the order of specializations goes like this:

Full sample can be found in

temp_fun.cpp
Depending of the order of functions (2) and (3) the output will be either
int mid(int a, int b, int c): 2
template<typename T> T mid(T a, T b, T c): 2
template<> double mid(double a, double b, double c): 2.4
template<> char* mid(char* a, char* b, char* c): hello
                    
if (2) is before (3), or
int mid(int a, int b, int c): 2
template<typename T> T mid(T a, T b, T c): 2
template<> double mid(double a, double b, double c): 2.4
template<typename T> T mid(T* a, T* b, T* c): hello                
                    
if (3) is before (2).

All samples were tested with gcc 4.8.1 and clang 3.3 under Linux.

Templates and statics

Static variable is instantiated per template instance. Function template

template<typename T>
void f(T t)
{
    static T s = t;
    s *= 2;
    if (typeid(T) == typeid(int))
        cout << "f<int>(T t): s=" << s << endl;
    else if (typeid(T) == typeid(double))
        cout << "f<double>(T t): s=" << s << endl;
    else
        cout << "f<T>(T t): s=" << s << endl;
}

int main()
{
    f(1);
    f(3);
    f(1.4);
    f(6.2);
    f(7);
    f(9.1);

    return 0;
}                
                    
is instantiated twice (for integer and double), so there are two static s variables for each of the instances:
f<int>(T t): s=2
f<int>(T t): s=4
f<double>(T t): s=2.8
f<double>(T t): s=5.6
f<int>(T t): s=8
f<double>(T t): s=11.2
                    
Class template is instantiated for integer and double objects (twice for both cases).
template<typename T>
class C
{
public:
    
    static void dbl()
    {
        t *= 2; 
        if (typeid(T) == typeid(int))
            cout << "C<int>: t=" << t << endl;
        else if (typeid(T) == typeid(double))
            cout << "C<double>: t=" << t << endl;
        else
            cout << "C<T>: t=" << t << endl;        
    }
    
    static T t;
};

template<typename T>
T C<T>::t;


int main()
{
    C<int> ci1, ci2;
    ci1.t = 1;
    ci1.dbl();
    ci2.dbl();
    C<double> cd1, cd2;
    cd2.t = 1.6;
    cd2.dbl();
    cd2.dbl();
    ci1.dbl();
    cd1.dbl();
    
    return 0;
}
                    
Static member is instantiated once for each of integer and double cases, so the output is:
C<int>: t=2
C<int>: t=4
C<double>: t=3.2
C<double>: t=6.4
C<int>: t=8
C<double>: t=12.8
                    

ready.

10 print "mail: contact at alepho.com | skype: karastojko | stackoverflow: karastojko | github: karastojko"
20 print "(c) 2009-2023 www.alepho.com"