Summary of C++ templates, plus some special usages (syntax) of templates, such as non-type template parameters, full specialization of template specialization, and a real understanding of why templates cannot be compiled separately

content

1. Non-type parameters of templates

2. Specialization of template classes and template functions (full specialization + partial specialization)

3. In-depth analysis of why templates do not support separate compilation

4. Generic programming for template STL--STL library summary

V. Summary of the article


1. Non-type parameters of templates

  • Templates that have been used for so long, generally all templates have type parameters, but unexpectedly, there are also non-type parameters in templates. That is, the parameters in the template are not necessarily a type, they can also be an integer, and only one can be passed in. Integer data. Integer constant ....
  • First let's think about the following piece of code ( throws the question )
#define N 100
template <class T>
class Array {
public:
	int getSize() {
		return N;
	}
private:
	T a[N];
};

int main() {
	//上述这样我们我们可以封装得到一个 可以管理 静态的 a[N]的数组的一个Array类....
	//但是这样我们要是需要其他容量的一个静态数组的类,不就每一次需要不停的重新定义N?????
	//这样使用起来会非常麻烦  
	Array<int> a1;
	a1.getSize();
    return 0;
}
  • How to solve the above problem??? See how the array source code in the STL library solves the problem.....   

The non-type template parameter size_t N.. is used in the above array class. Then the following code can explain the benefits:   

template <class T, size_t N>
class Array {
public:
	int getSize() {
		return N;
	}
private:
	T a[N];
};

int main() {
	Array<int, 10> a1;
	cout << a1.getSize() << endl;
	Array<int, 100> a2;
	cout << a2.getSize() << endl;
    return 0;
}

At this point, the above N is actually equivalent to an integer constant, so we don't need to define a global macro, which is convenient for specifying the length of the static array.

2. Specialization of template classes and template functions (full specialization + partial specialization)

  • The official explanation of template specialization : To provide a specialization version in the generalization design (that is, to give explicit designation to some or all parameters in the generalized version), all explicit designation is full specialization, and some explicit designation is partial specialization ....
  • My understanding: the specialization of the template is to directly and normally let the compiler instantiate the template in a special scenario and cannot obtain the effect we want to achieve. At this time, we will give a special scenario for this scenario. Root specialization instance version. To handle this case...
  • Specialized versions of special scenarios are simulated as follows. For example, if we want to compare whether strings are the same in a template function, we can directly write the code and test the results as follows (specialized versions are not provided)....
    template<class T>
    bool IsEqual(const T& a, const T& b) {
    	return a == b;
    }
    int main() {
    	cout << IsEqual(1, 1) << endl;
    	cout << IsEqual(1.1, 1.1) << endl;
    	char s1[] = "1234";
    	char s2[] = "1234";
    	cout << IsEqual(s1, s2) << endl;
    	char *s3 = "1234";
    	char *s4 = "1234";
    	cout << IsEqual(s3, s4) << endl;
    	while (1);
    	return 0;
    }

    The result of the above code needs to be tested by the reader. The result is also very simple and cannot meet the requirements. The function of comparing and equalizing the template for strings cannot be achieved. What should we do? Provide a special version instance to support the results we want.

The result of providing a specialized version is as follows  

template<class T>
bool IsEqual(const T& a, const T& b) {
	return a == b;
}
//针对字符串类型进行特殊处理 -- 写一份特例出来
bool IsEqual(const char* s1, const char* s2) {
	return strcmp(s1, s2) == 0;
}
int main() {
	cout << IsEqual(1, 1) << endl;
	cout << IsEqual(1.1, 1.1) << endl;
	char s1[] = "1234";
	char s2[] = "1234";
	cout << IsEqual(s1, s2) << endl;
	char *s3 = "1234";
	char *s4 = "1234";
	cout << IsEqual(s3, s4) << endl;
	while (1);
	return 0;
}

The above is the specialization of the function template, followed by the specialization of the class template.... The first is the syntax form. It looks like the following code snippet:

template <>               //<>不一定是空, 空代表全特化, 否则偏特化					
class testClass <type, type, ...> { // <type, type, ...> 就是具体的特化类型....

};

Then there is the actual case of full specialization : (without pasting the case results, I hope readers can verify by themselves and debug which class entered)

//测试类模板
template <class T1, class T2> 
class TestClass {
public:
	TestClass() {
		cout << "T1, T2" << endl;
	}
};
//模板特化案例
template<>
class TestClass<int, int> {					//特化一个int,int 版本调用这个
public:
	TestClass() {
		cout << "int, int" << endl;
	}
};
//hash类模板
template <class Key>						 
struct hash{};
//模板的特化案例
template<>
struct hash<char> {
	size_t operator ()(char x) const { return x; }
};

template<>
struct hash<int> {
	size_t operator ()(int x) const { return x; }
};

template<>
struct hash<long> {
	size_t operator ()(long x) const { return x; }
};

int main() {
	cout << hash<int>()(1000) << endl;
	cout << hash<int>()('a') << endl;//可通过 f9断点 + f10 f11测试进入类
	TestClass<int, int> obj1;
	TestClass<int, double> obj2;
}
  • partial specialization, partial specialization of templates -- the first type (number of partials)
  • The following is still a quantitative bias , readers can test by themselves
  • template <class T1, class T2> 
    class TestClass {
    public:
    	TestClass() {
    		cout << "T1, T2" << endl;
    	}
    };
    
    template<class T1>
    class TestClass<T1, int> {//只要T2是int走这个
    public:
    	TestClass() {
    		cout << "T1, int" << endl;
    	}
    };
    
    template<class T1>
    class TestClass<T1, char> {//只要T2是char走这个
    public:
    	TestClass() {
    		cout << "T1, char" << endl;
    	}
    };
    //上述是仅仅针对T2做的特化处理
    int main() {
    	TestClass<char, char> t1;
    	TestClass<char, int> t2;
    	TestClass<char, double> t3;
    	return 0;
    }
  •  partial speciation, template partial specialization -- partial specialization of the range (I hope readers can test and analyze it by themselves)
    //测试类模板
    template <class T1, class T2> 
    class TestClass {
    public:
    	TestClass() {
    		cout << "T1, T2" << endl;
    	}
    };
    
    //如下是范围上的偏特化, 数量上是全部, 但是范围进针对指针或者引用类型
    template<class T1, class T2>
    class TestClass<T1*, T2*> {
    public:
    	TestClass() {
    		cout << "T1*, T2*" << endl;
    	}
    };
    
    template<class T1, class T2>
    class TestClass<T1&, T2&> {
    public:
    	TestClass() {
    		cout << "T1&, T2&" << endl;
    	}
    };
    
    int main() {
    	TestClass<int*, char*> t4;
    	TestClass<int&, char&> t5;
    	return 0;
    }

3. In-depth analysis of why templates do not support separate compilation

  • Since it is to analyze why the template does not support separate compilation, then we try to separate compilation to see what errors are reported.
  • The first piece of Vector.h code:
    #pragma once
    #include <iostream>
    using namespace std;
    //只是放置申明....
    //放置类申明
    template <class T> 
    class Vector {
    public:
    	Vector();
    };
    //放置函数申明
    template <class T>
    T add(const T& left, const T& right);
  • The second paragraph is the code of Vector.cpp
    #include "Vector.h"
    
    //实现....
    template <class T>
    Vector<T>::Vector() {
    	cout << "构造" << endl;
    }
    
    //放置函数申明
    template <class T>
    T add(const T& left, const T& right) {
    	return left + right;
    }
  • The third paragraph is the code of test.cpp
    #include "Vector.h"
    int main() {
    	return 0;
    }

    It is found that there is no error when compiling without object construction or calling any function. It is over, indicating that there is no grammatical error. …

 As found above, there is no error message during the compilation phase when compiling separately.

  • Now create an object or call a template function to instantiate it to see the effect???
  •  Test the guess separately...    Both of them have the same error phenomenon, the unparseable external instruction, what does it mean, and the reason for separating the compilation error is whether there is no function definition implementation, only function declaration. . . .   

  • Why  only the function declaration has no function implementation, this kind of error can only be found when linking , especially when compiling separately   
  • The process of separate compilation is drawn as follows....

 The above is just a guess, but it should be correct. To verify whether the instantiation of the template is missing in Vector.cpp, the solution 1. It can also be considered to verify the conjecture. We manually add an explicit instance of the template in Vector.cpp change.

 The above method can solve the separate compilation of template functions, but it is quite troublesome, and I don't know how to deal with the instantiation of class display. Anyone who knows can help to inform the comment area. 

  • The most appropriate processing for template classes and template functions, do not perform separate compilation, and declare definitions in a .hpp file to complete

4. Generic programming for template STL--STL library summary

advantage :

  • Reusing code, in principle, the dirty work is all left to the compiler to do
  • Templates allow some of our code to be flexible

shortcoming :

  • Templates seem to save code, but there is a problem of code bloat after actual compilation
  • vector<int> v1; vector<double> v2, vector<string> v3.... Of course, in order to reduce code bloat as much as possible, the compiler will instantiate as needed and call the member function
  • After a syntax error occurs in the template, sometimes the error is confusing and inaccurate, and it is not easy to locate the error location. Generally, only the first error is seen, and most of the following ones are inaccurate.
  • Templates do not support separate compilation

V. Summary of the article     

First of all, the four points of this article: 

1. Non-type template parameters: array template<class T, size_t N> template parameters in the library can be integer constants  

2. Template specialization: specialization reasons ( for special scenarios, conventional template instantiation cannot get the results we want, this time give a specialization instance ), specialization type ( full specialization, partial specialization: quantity on, range on )

3. From the essence of why templates do not support separate compilation: Starting from the separate compilation process, the two .o files interact only when they are linked. In the previous compilation stage, there was no interaction, and the template in Vector.cpp could not be instantiated. ..

4. Summary of advantages and disadvantages of templates Advantages, convenience. Code reuse, compiler help examples, disadvantages: start with code expansion

As a brief summary of the above, if you can recall it, or you can clearly answer the above questions in your mind, it is ok. If you are confused, you can go back and look back. You may have just been distracted.

In the end, this article contains some lessons from Mr. Hou Jie's courseware, as well as my own notes and insights, which are only used as personal notes for learning to share common progress with everyone... If you feel that this article is insufficient, please leave a message in the comment area, I wish common learning and progress

Guess you like

Origin blog.csdn.net/weixin_53695360/article/details/122783549