C + + generic programming: non type template parameters, template specialization, separate compilation of templates

Posted by shamil on Fri, 24 Dec 2021 10:18:03 +0100

Non type template parameters

There are two types of template parameters:

  • Type parameter: This is the method we usually use, that is, add the type name of the parameter after class in the parameter list of the template.
  • Non type parameter: a constant is used as a parameter of the template, which can be used as a constant in the template. It is usually used only when the size or initialization content needs to be specified.

Type parameters we In the last blog It's very detailed. I won't repeat it. The common non type parameters are array in c + +:

The bottom layer of array is the directly used array, and the size must be specified when creating the array, and the size must be a constant, so non type template parameters will be used.

be careful:

  1. Floating point numbers, custom types, class objects, and strings are not allowed as non type template parameters.
  2. Non type parameters must be able to confirm the results at compile time.

In general, non type template parameters use character and integer types.

Specialization of function templates

When we use a template to implement a function, we certainly want to use it to solve some problems with the same logic but different data types to realize code reuse, but there are also some special cases. For example, for a certain scenario or a certain type, the template needs special processing. At this time, we need to use the specialization of the template.

For example:

template<class T>
bool IsEqual(T str1, T str2)
{
	return str1 == str2;
}

int main()
{
	char str1[] = "hello";
	char str2[] = "hello";

	if (IsEqual(str1, str2))
		cout << "true";
	else
		cout << "false";
}

The reason for the difference here is that two char * types were passed in the past. They compare not the content of the string, but the address of the pointer. Here, str1 and str2 open up a space on the stack and then copy the hello, while IsEqual compares the first addresses of the two different memories (that is, the two hello memories) pointed to by the two. They can't be the same.

If you want to compare char *, you have to use strcmp to deal with this situation, that is, the specialization of the template.

template<>
bool IsEqual<char*>(char* str1, char* str2)
{
	return strcmp(str1, str2) == 0;	
}
// extern int strcmp(const char *s1,const char *s2);


Specialization steps of function template:

  • You must have a basic function template first
  • The keyword template is followed by a pair of empty angle brackets < >
  • Specify the type to be specialized in < > after the function name
  • The formal parameters of a specialized function must be exactly the same as those of the template.

class template specialization

The same is true for classes. If special situations are needed, they also need special treatment:

Class templates are as follows:

template<class T1, class T2>
class test
{
public:
	test()
	{
		cout << "test<T1, T2>" << endl;
	}

private:
	T1 _x;
	T2 _y;
};

Full specialization

Full specialization is to determine all parameters in the template parameter list.

The version of test < int, double > is specialized here.

template<>
class test<int, double>
{
public:
	test()
	{
		cout << "test<int, double>" << endl;
	}

private:
	int _x;
	double _y;
};

int main()
{
	test<double, double> t1;
	test<int, double> t2;
}

Partial specialization

Partial specialization is any specialized version of further conditional design for template parameters.

Partial specialization can be expressed in two ways: partial parameter specialization and parameter modification specialization.

Partial parameter specialization

Here, the second parameter is specialized. As long as the second parameter is double, the corresponding specialized version will be called.

template<class T1>
class test<T1, double>
{
public:
	test()
	{
		cout << "test<T1, double>" << endl;
	}

private:
	T1 _x;
	double _y;
};

int main()
{
	test<double, double> t1;
	test<int, double> t2;
	test<float, double> t3;
	test<double, int> t4;
}

Parameter modification specialization

For example, using pointers or references to modify types can also be specialized.

template<class T1, class T2>
class test<T1*, T2*>
{
public:
	test()
	{
		cout << "test<T1*, T2*>" << endl;
	}

private:
	T1* _x;
	T2* _y;
};

int main()
{
	test<double, int> t1;
	test<int*, double*> t2;
	test<float*, double*> t3;
	test<char*, double> t4;
}	

Template separation compilation

For a project with a large amount of code, the method of separation of declaration and definition is usually adopted, such as declaration in the header file, code implementation in the source file, and finally linked into a single executable file through the link method. However, the C + + compiler does not support the separation compilation of templates. Once the separation compilation is carried out, a link error will occur.

//Header file a.h
template<class T>
bool IsEqual(const T& str1, const T& str2);

-------------
//Source file a.cpp
template<class T>
bool IsEqual(const T& str1, const T& str2)
{
	return str1 == str2;
}
--------------
//test.c
#include<iostream>
#include"a.h"
using namespace std;

int main()
{
	cout << IsEqual(3, 5);

	cout << IsEqual('a', 'b');
}

There seems to be no problem here, but it involves the instantiation rules of the template.

When the main function calls this function, it will find the function declaration in the header file (a.h), and then find the implementation in a.cpp through the declaration.

However, this is not the case for templates. As mentioned in the previous chapter, the instantiation of templates will only be carried out when they are used for the first time. For example, IsEqual(3, 5) here will look in the header file, but there are only declarations and no definitions in the header file, so it is impossible to instantiate them. He also wants to instantiate by finding the function definition in a.cpp, but unfortunately, there is only the definition of IsEqual (const T & STR1, const T & STR2) in a.cpp, but there is no instantiation of IsEqual. (this type of instance is not used in a.cpp, so naturally it will not be instantiated.) at this time, the implementation of this function cannot be found in test.c, resulting in link failure.

resolvent

There is no perfect solution to this problem

  • Put the declaration and definition in the same header file. (causing the header file code to be too large)
  • The location of the template definition is explicitly instantiated. (not practical)

Liu weipeng wrote this question very well. You can learn from his blog

Why can't the C + + compiler support separate compilation of templates

Topics: C++ C#