Think In Geek

In geek we trust

Crazy stuff in C++ (1)

Introduction

C++ is a controversial language: you love it or you hate it. As always, knowing better about something allows one to make better arguments for or against that thing. This is what this series is about. Here I’ll explain some of the less known (except for C++ expert programmers, of course) features of C++.

Let’s start with templates, which account for such a huge part of the language.

Templates

Everyone knows that C++ has templates. Templates is an implementation of the «I have an algorithm that can be used with many different types» idea. This idea is called generic programming and is a pretty powerful one. This is why it is present in almost all modern languages.

Back to C++. C++ defines two kinds of templates: classes templates and function templates. Class templates define an infinite set of classes while function templates define an infinite set of functions. The elements of these sets of classes or functions are called specializations. Every template has a template-name which will be used to name a specific specialization.

Template declarations

Consider these two declarations

1
2
template <typename T>
struct my_list { ... }
1
2
template <typename T>
void max(T a, T b) { return a > b ? a : b; }

These are template declarations. The first one declares a class template and its template-name is my_list, the second one defines a function template and its template-name is max. A template declaration is just a declaration preceded with something without an official name that starts with template <…>, I will call it the template header (but note that this name is not blessed by the C++ standard at all, it just makes sense to me call it like this).

The template header defines what are the parameters of the template class. These are called the template parameters. A type-template parameter, like that T shown above, is a “type variable”. This is the most usual template parameter as it allows to parameterize the declaration over one or more type variables. C++, though, has two more kinds of template parameters: nontype-template parameters and (the funny named) template-template parameter. A nontype-template parameter allows us to parameterize the declaration over a (compile-time) constant integer value. Here “integer value” is a very broad term: of course it includes all integers, but also enum values (enumerators) and addresses of (statically allocated) variables and functions. A template-template parameter allows us to parameterize a declaration over another class template with appropiate template parameters.

1
2
template <typename T, int N> // N is a nontype-template parameter
struct my_fixed_array { };
1
2
template <template <typename T> MyContainer> // MyContainer is a template-template parameter
struct adaptor { };

Specializations

I said above that a class template or function template defines an infinite set of classes or function and that each element of that set was called a specialization. There is a specialization for every possible value that a template parameter can have. Such values are not bounded thus there is an infinite number of specializations (well, we could argue that constant integer values are finite in the language, but types are clearly not finite).

We give value to template parameters of a template by means of template arguments. These template arguments always appear in what is called a template-id. A template-id is just the template-name followed by a list of template-arguments enclosed in < and >.

1
2
my_list<int> l;// Here T has value int, we will write it as T ← int
max<float>(3.4f, 5.6f); // T ← float

Primary template and partial specializations

When we first declare a class template or a function template, such declaration defines the primary template.

1
2
template <typename T>
struct my_list { ... };
1
2
template <typename T>
void swap(T& a, T& b);

Class templates (but not function templates!) can have an extra set of template declarations called partial specializations. A partial specialization looks like a normal class template declaration but the template-name is now a template-id where the template-arguments partially specialize the given template parameters.

1
2
3
4
5
6
7
8
9
10
11
12
// 1) Partial specialization for "pointer to (any) P" type
template <typename P>
struct my_list<P*> { }; 
 
// 2) Partial specialization for "array of (any) Size of (any) Element"
template <typename Element, int Size>
struct my_list<Element[Size]> { };
 
// 3) Partial specialization for "pointer to function with two parameters 
// Arg1 and Arg2 returning Ret"
template <typename Ret, typename Arg1, typename Arg2>
struct my_list<Ret (*)(Arg1, Arg2)>;

A C++ compiler will always pick the partial specialization (if any) that is “closer” to the one requested in the template arguments. If no partial specialization matches, the primary template is chosen instead. The exact algorithm is not important here.

1
2
3
4
5
6
7
8
9
10
my_list<int> l0; // will pick the primary template T ← int
 
my_list<int*> l1; // will pick partial specialization 1) 
// where P ← int (note that respect to the primary template this is T ← int*)
 
my_list<int[10]> l2; // will pick partial specialization 2) 
// where Element ← int and Size ← 10
 
my_list<int (*)(float, double)> l3; // will pick partial specialization 3) 
// where Ret ← int, Arg1 ← float and Arg2 ← double

I think this is enough for today regarding C++ templates. More craziness to come. Stay tuned.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

, , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *