[C++] Advanced template, get generic programming

1. The concept of templates

Template concept:

  • The template realizes the code of the common type, and does not need to care about the type of the data, so the C++ standard template library STL is produced. Templates can be divided into: class templates and function templates. The type deduction and specific code generation are both in the compilation stage.
    • Template without instantiation: The compiler just performs a simple syntax check on the template, and does not generate code
    • After instantiation: Generate code according to the type of template instantiation by the user, and perform grammar check on the generated code

Parameters of template type:

  • Type template parameter: if the type is uncertain, the corresponding code will be generated after instantiation
  • Non-type template parameter: the type has been materialized
    • If there is a default or a parameter, it must be a constant, because the size must be determined at the compilation stage
    • Floating point numbers, class objects, and strings are not allowed as non-type template parameters
template <class T, size_t N = 5>	// 非类型模板参数缺省时,必须是常数
class arr
{
    
    
public:
	arr()
		:_size(0)
	{
    
    }
	T& operator[](const int index){
    
    return _arr[index];}
	void insert(const T& data){
    
    _arr[_size++] = data;}
private:
	T _arr[N];
	size_t _size;
};
int main()
{
    
    
	arr<int, 10> a;	// 非类型模板参数传参时必须是常数,否则报错
	return 0;
}

The compilation and linking process of the template:

  • There are the following addition template functions: function declaration: in the ah header file, function definition: in a.cpp
a.h头文件:加法模板的声明
#pragma once
template <class T>
T Add(const T& left, const T& right);

a.cpp源文件:加法模板的定义
#include "a.h"
template <class T>
T Add(const T& left, const T& right)
{
    
    
	return left + right;
}

main.cpp 
#include "a.h"
int main()
{
    
    
	int sum = Add(1, 2);
	return 0;
}

Note: This code will cause an error during the linking process, and the int type template function address can not be found!

  • Reason: The separate compilation of the template does not generate the corresponding type of code, resulting in the function address entry cannot be found when linking
    • Compile: Check the correctness of the grammar of the program, and generate assembly code after checking the correctness. Note: The header file does not participate in the compilation, and the compiler compiles multiple source files in a project separately.
    • Link: Combine two .obj object files into one, deal with the address problem in the unresolved symbol table, and find the corresponding function address in the exported symbol table of other source files.
  • Separate compilation: There are multiple source files for the same project, and each source file is a separate target file, and finally all the files are linked to form a single executable file.
    • When a.cpp source file is compiled, the compiler does not see the instantiation of the Add template function, so it will not generate specific int type code
    • When the main.obj target file is connected, go to other target files to find the function address of Add<int>, but the link error is reported because it is not found.

solve:

  1. Show instantiation at the template definition, not recommended
  2. Put the template declaration and definition in the same .hpp header file
1.在模板定义处显示实例化
#include "a.h"
template <class T>
T Add(const T& left, const T& right)
{
    
    
	return left + right;
}
void Test()
{
    
    
	Add<int>(1, 2);
	Add<double>(1.2, 2.3);
}

2.放在同一头文件中
#pragma once
template <class T>
T Add(const T& left, const T& right);

template <class T>
T Add(const T& left, const T& right)
{
    
    
	return left + right;
}

Second, the specialization of templates

Function template specialization:

  • The following implements a common type of comparison function, which returns the larger number of the two.
template <class T>
T& Max(T& l, T& r)
{
    
    
	return l > r ? l : r;
}
int main()
{
    
    
	int a = 1, b = 2;
	cout << Max(a, b) << endl;
	return 0;
}
  • It can be executed correctly for int, double and other types, but if the string type is passed in, the comparison is the address size, which is different from expected, so the function template specialization method is used to handle the special type

solve:

  1. Directly overload special types of functions, which is relatively simple and recommended
  2. Perform template function specialization
template <class T>
T& Max(T& l, T& r)
{
    
    
	return l > r ? l : r;
}
// 进行函数重载
const char* Max(const char* l, const char* r)
{
    
    
	if (strcmp(l, r) == 1)
		return l;
	else
		return r;
}
// 进行特化处理:重载参数存在 & 的函数模板
template <>
const char*& Max<const char*>(const char*& l, const char*& r)
{
    
    
	if (strcmp(l, r) > 0)
		return l;
	else
		return r;
}
int main()
{
    
    
	int a = 1, b = 2;
	cout << Max(a, b) << endl;
	const char* ptr1 = "bsd";
	const char* ptr2 = "acv";
	cout << Max(ptr1, ptr2) << endl;
	return 0;
}

Function template specialization method:

  1. Add an empty angle bracket <> after the keyword template
  2. Add angle brackets after the function name, the type of pointer specialization in the angle brackets
  3. Function parameter list: the same as the basic parameter type of the template function

Class template specialization:

  • Class template specialization is divided into: full specialization and partial specialization
    • Full specialization: all parameters in the template parameter list are determined
    • Partial specialization: further restrictive conditions for the default parameters to form a specialization version
#include <iostream>
using namespace std;
template <class T1, class T2>
class Date
{
    
    
public:
	Date() {
    
     cout << "Date<T1, T2>" << endl; }
	T1 _day;
	T2 _week;
};

// 全特化版本
template <>
class Date <int, char>
{
    
    
public:
	Date() {
    
     cout << "Date<int, char>" << endl; }
	int _day;
	char _week;
};

// 偏特化:1.模板部分参数特化
template <class T1>
class Date <T1, char>
{
    
    
public:
	Date() {
    
     cout << "Date<T1, char>" << endl; }
	T1 _day;
	char _week;
};

// 偏特化:2.对参数进行进一步限制
template <classT1, class T2>
class Date <T1*, T2*>
{
    
    
public:
	Date() {
    
     cout << "Date<T1*, T2*>" << endl; }
	T1 _day;
	T2 _week;
};
int main()
{
    
    
	Date<int, char> d1;		// 走全特化版本
	Date<int, int> d2;		// 走基础模板
	Date<char, char> d3;	// 走特化char版本
	Date<int*, int*>d4;		// 走特化指针版本
	
	return 0;
}

Specialization method:

  • Processing in angle brackets after the keyword template, full specialization: empty, partial specialization: add those that do not require specialization, and limit parameters: add limit parameters
  • Add angle brackets and specialization after the class name

3. Type extraction

  • Specialized application of class template: type extraction

The following implements a generic type of copy function:

template <class T>
void Copy(T& dst, const T& src, size_t size)
{
    
    
	// 内存拷贝:优点:效率高  缺点:设计内存资源管理是浅拷贝
	memcpy(dst, src, size * sizeof(T));

	// 深拷贝:优点:一定不会出错  缺点:效率太低
	for (int i = 0; i < size; i++)
		dst[i] = src[i];
}
  • Use memcpy memory copy: the copy efficiency is high, and there is no error for the built-in type. But for data designed for dynamic memory, it will cause memory leaks, and even cause program crashes due to double free
  • Using deep copy method: copy efficiency is too low, but deep copy will not cause errors

Therefore, use class template specialization: type extraction, extract built-in types, and perform memory copy

// 类型萃取
struct True_Type
{
    
    
	static bool Get(){
    
    return true;}
};
struct False_Type
{
    
    
	static bool Get(){
    
    return false;}
};
// 其它类型
template <class T>
struct TypeTraits
{
    
    
	typedef False_Type PODTYPE;
};
// 内置类型:特化处理
template<>
struct TypeTraits<int>
{
    
    
	typedef True_Type PODTYPE;
};
template<>
struct TypeTraits<char>
{
    
    
	typedef True_Type PODTYPE;
};
template<>
struct TypeTraits<double>
{
    
    
	typedef True_Type PODTYPE;
};
template<>
struct TypeTraits<short>
{
    
    
	typedef True_Type PODTYPE;
};

// 对拷贝函数进行重载
template <class T>
void Copy(T* dst, const T* src, size_t size, True_Type)
{
    
    
	// 内置类型
	memcpy(dst, src, size * sizeof(T));
}
template <class T>
void Copy(T* dst, const T* src, size_t size, False_Type)
{
    
    
	// 其他类型
	for (size_t i = 0; i < size; i++)
		dst[i] = src[i];
}
template <class T>
void Copy(T* dst, const T* src, size_t size)
{
    
    
	Copy(dst, src, size, TypeTraits<T>::PODTYPE());
}
int main()
{
    
    
	int arr1[] = {
    
     1,2,3,4 };
	int arr2[4] = {
    
     0 };
	Copy(arr2, arr1, 4);

	String s[] = {
    
     "111", "222", "333" };
	String p[3];
	Copy(p, s, 3);
	return 0;
}

String class

class String
{
    
    
public:
	// 构造
	String(const char* str = "")
	{
    
    
		if (str == nullptr)
			str = "";
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	// 拷贝构造
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
    
    
		strcpy(_str, s._str);
	}
	// 析构
	~String()
	{
    
    
		if (_str)
		{
    
    
			delete[] _str;
			_str = nullptr;
		}
	}
	// 赋值运算符重载:现代法
	String& operator=(String s)
	{
    
    
		swap(_str, s._str);
		return *this;
	}
private:
	char* _str;
};

Guess you like

Origin blog.csdn.net/qq_45691748/article/details/114294119