📜 ⬆️ ⬇️

C ++ Pattern Specialization Tricks

image Template specialization is one of the “complex” features of the C ++ language and is mainly used when creating libraries. Unfortunately, some features of the specialization of templates are not well disclosed in popular books on this language. Moreover, even the 53 pages of the official ISO standard of the language, devoted to templates, describe the interesting details of the messy, leaving a lot to "guess yourself - it's obvious." Under the cat, I tried to clearly state the basic principles of the specialization of patterns and show how these principles can be used in the construction of magic spells.


Hello world


How are we used to using templates? Use the template keyword, then in angle brackets the names of the template parameters , followed by the type and name. For parameters, also indicate what it is: type (typename) or value (for example, int). The type of the template itself can be a class (class), a structure (a struct - in general, also a class) or a function (bool foo () and so on). For example, the simplest template class 'A' can be set like this:
image
After a while, we want our class to work the same for all types, and for some sly like int - differently. Garbage question, we write a specialization: it looks the same as an ad, but we don’t specify the template parameters in angle brackets, instead we specify the specific arguments of the template after its name:

 template <> class A <int> {};  // here, int is the template argument

Done, you can write special implementation methods and fields for int. Such specialization is usually called full specialization or explicit specialization. For most practical tasks, no more is required. And if required, then ...
')

A custom template is a new template.


If you carefully read the ISO standard C ++, you can find an interesting statement: by creating a specialized template class, we create a new template class (14.5.4.3). What does this give us? A specialized template class can contain methods whose fields or type declarations are not in the template class that we specialize. Conveniently, when it is necessary that the template class method works only for a specific specialization - it is enough to declare a method only in this specialization, the compiler will do the rest:
method only in specialization

Specialized template can have its own template parameters.


The devil is known to be in the details. The fact that the specialized template class is a very, very new and separate class is certainly interesting, but there is little magic in it. And the magic is in a minor consequence - if it is a separate template class, then it can have separate parameters that are not related to the non-specialized template class (parameters are that after the template in angle brackets). For example, like this:

 template <typename S, typename U> class A <int> {};

However, it is this code that the compiler does not compile - we don’t use the new parameters of the template S and U, which is forbidden for the specialized template class (and the fact that it is the specialized compiler class understands because it has the same name 'A' as the one already declared template class). The compiler will even say a special error: “explicit specialization is using partial specialization syntax, use template <> instead”. It hints that if you have nothing to say, then you should use the template <> and not show off. Then why is it possible to use new parameters in a specialized template class? The answer is strange - in order to specify the specialization arguments (the arguments are what after the class name in angle brackets). That is, by specializing the template class, we can, instead of simple and clear int, specialize it through new parameters :

 template <typename S, typename U> class A <std :: map <S, U>> {};

Such a strange record will compile. And when using the resulting template class with std :: map, specialization will be used, where the key type std :: map will be available as a parameter of the new template S, and the value type std :: map will be U.

Such a template specialization, in which a new list of parameters is specified, and through these parameters, the arguments for specialization are specified is called partial specialization. Why "partial"? Apparently because it was originally conceived as a syntax for the specialization of the template not for all arguments. An example where a template class with two parameters specializes only in one of them (the specialization will work when the first argument, T, is specified as int. The second argument can be any — for this, a new parameter U is entered in the partial specialization and is listed arguments for specialization):

 template <typename T, typename S> class B {};
 template <typename U> class B <int, U> {};

The magical effects of partial specialization


Of the two properties of the template specialization described above, there are a number of interesting implications. For example, when using partial specialization, it is possible, by entering new template parameters and describing specialized arguments through them, to break composite types into simplest ones. In the example below, the specialized template class A will be used if the template argument is a type of function pointer. At the same time, through the new parameters of the S and U template, you can get the type of the return value of this function and the type of its argument:

 template <typename S, typename U> class A <S (*) (U)> {};

And if you declare typedef or static const int in a specialized template (using the fact that this is a new template), you can use it to extract the necessary information from the type. For example, we use a template class to store objects and want to get the size of the passed object or 0 if it is a pointer. In two lines:

 template <typename T> struct Get {const static int Size = sizeof (T);  };
 template <typename S> struct Get <S *> {const static int Size = 0;  };

 Get <int> :: Size // for example, 4
 Get <int *> :: Size // 0 - found the pointer :)

This type of magic is mainly used in libraries: stl, boost, loki, and so on. Of course, when using high-level programming, it’s fun to use such tricks - I think we all remember the construction to get the size of the array :). But in libraries, partial specialization makes it relatively easy to implement delegates, events, complex containers, and other sometimes very necessary and useful things.

Colleagues, if you find a mistake (and I, unfortunately, are not a guru, I could be mistaken) or you have criticism, questions, or additions to the above, I will be glad to comment.

Update: The promised continuation here .

Source: https://habr.com/ru/post/54762/


All Articles