C + + improved programming -- 1.1 function template [P167~P173]

Posted by astarmathsandphysics on Sun, 19 Dec 2021 07:19:14 +0100


[C + + improve programming mainly for C++ Generic programming and Explain STL technology in detail and explore the deeper application of C + +

1 template

Template is to establish a general mold, which greatly improves the reusability.
Characteristics of formwork:

  • The template can't be used directly. It's just a framework;
  • Although the template is universal, it is not omnipotent.

1.1 function template

  • Another programming idea of C + + is called generic programming, and the main technology is template
  • C + + provides two template mechanisms: function template and class template

1.1. 1 function template syntax

Function template function:
Establish a general function whose function return value type and formal parameter type can be represented by a virtual type without specific formulation.

The functions to realize integer and floating-point data exchange are as follows:

# include<iostream>
using namespace std;

// Function template
// Swap two integer functions
void swapInt(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}

// Swap two floating-point functions
void swapDouble(double &a, double &b)
{
	double temp = a;
	a = b;
	b = temp;
}

void test01()
{
	int a = 20;
	int b = 10;
	swapInt(a,b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;


	double c = 1.1;
	double d = 2.2;
	swapDouble(c, d);
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}


Although the above code can realize the corresponding functions, what if you can realize the exchange of all data types? Obviously, it's too low to implement them one by one. Moreover, there will be user-defined data types. If you want to implement them all, it will be too weak.

By observing the exchange of integer and floating-point functions, it can be found that the difference between the two functions lies in the data type, and the code implementation is the same. Therefore, we can first represent the data type with a capital letter (such as T), and then tell what the data type should be in later use.

The following is the implementation method of the function template:

# include<iostream>
using namespace std;

// Function template
template<typename T>	// Declare a template and tell the compiler not to report an error for the T immediately following the code. T is a general data type
void mySwap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}
// Exchange using function template
void test02()
{
	// There are two ways to call function templates
	// 1. Automatic type derivation
	int a = 20;
	int b = 10;
	mySwap(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	// 2. Displays the specified type
	double c = 1.1;
	double d = 2.2;
	mySwap<double>(c, d);
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
}

int main()
{
	test02();
	system("pause");
	return 0;
}


Summary:

  • The function template uses the keyword template;
  • There are two ways to use function templates: automatic type derivation and display of specified types;
  • The purpose of template is to improve reusability and parameterize types.

1.1. 2. Precautions for function template

matters needing attention:

  • For automatic type derivation, a consistent data type T must be derived before it can be used
  • The template can be used only after the data type of T is determined

1.1. 3 function template cases

# include<iostream>
using namespace std;

// Swap function template
template<class T>
void mySwap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

template<class T>
void mySort(T arr[],int len)
{
	for (int i = 0; i < len; i++)
	{
		int max = i;
		for (int j = i + 1; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i)
		{
			mySwap(arr[max], arr[i]);
		}
	}

}

// Provide print array template
template <class T>
void printArray(T arr[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void test01()
{
	// Test char array
	char charArr[] = "badcfe";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}
void test02()
{
	// Test int array
	int intArr[] = {7, 5, 1, 3, 9, 2, 4, 6, 8};
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

1.1. 4 difference between ordinary function and function template

Automatic type conversion = = implicit type conversion

1. Implicit type conversion can occur when a normal function is called

# include<iostream>
using namespace std;
 // Ordinary function
int myAdd01(int a, int b)
{
	return a + b;
}
void test01()
{
	int a = 10;
	int b = 20;
	cout << myAdd01(a, b) << endl;

	char c = 'a';
	cout << myAdd01(a, c) << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}


It can be seen that when the input parameter is a char variable, the program can also run normally. During the operation, the character variable is implicitly converted into an integer variable (corresponding Ascall code) for calculation.

2. Function templates cannot have implicit type conversion when using automatic type derivation, but can have implicit type conversion by using the specified type

It can be found that the function template reports errors when using automatic type derivation, but does not report errors when using the specified type.

1.1. 5 call rules for ordinary functions and function templates

# include<iostream>
using namespace std;
// Ordinary function
void myPrint(int a, int b)
{
	cout << "Ordinary function called" << endl;
}
// Function template
template<class T>
void myPrint(T a, T b)
{
	cout << "Called function template" << endl;
}

// Overloaded function template
template<class T>
void myPrint(T a, T b, T c)
{
	cout << "Overloaded function template called" << endl;
}

void test01()
{
	int a = 10;
	int b = 20;
	// 1. If both normal functions and function templates can be implemented, call normal functions first
	myPrint(a, b);
	// 2. You can force a function template to be called through an empty template parameter list
	myPrint<>(a, b);
	// 3. Function overloading can also occur in function templates
	myPrint(a, b, 100);
	// 4. If the function template can produce a better match, call the function template first
	char c1 = 'a';
	char c2 = 'c';
	myPrint(c1, c2);
	/*
		We know that although ordinary functions can have implicit type conversion, the function template here is more matched and there is no need for implicit type conversion, so we give priority to calling the function template
	*/
}


int main()
{
	test01();
	system("pause");
	return 0;
}


Summary: since the function template has been provided, it is better not to provide ordinary functions, otherwise ambiguity is likely to occur

1.1. 6 limitations of formwork

Limitations: the universality of the template is not omnipotent!!

Therefore, in order to solve this problem, C + + provides template overloading, which can provide specific templates for these specific types

# include<iostream>
# include<string>
using namespace std;

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

// Template limitations
// Templates are not omnipotent. Some specific data types need to be implemented in a specific way

template<class T>
bool myCompare(T &a, T &b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}
// Use the version of materialized Person to realize the code, and materialization is called first
template<> bool myCompare(Person &p1, Person &p2)
{
	if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age)
	{
		return true;
	}
	else
	{
		return false;
	}
}

void test01()
{
	int a = 10;
	int b = 20;

	bool ret = myCompare(a, b);

	if (ret)
	{
		cout << "a == b" << endl;
	}
	else
	{
		cout << "a != b" << endl;
	}
}

void test02()
{
	Person p1("Tom", 10);
	Person p2("Tom", 10);
	bool ret = myCompare(p1, p2);
	if (ret)
	{
		cout << "p1 == p2" << endl;
	}
	else
	{
		cout << "p1 != p2" << endl;
	}
}

int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}

Summary:

  • The generalization of user-defined types can be solved by using specific templates;
  • Learning templates is not to write templates, but to use the templates provided by the system in STL.