《Beginning C++17》-学习笔记-Chapter 09-Functions Templates

本文深入探讨了C++中模板函数的应用,包括泛型编程、特化、重载、默认参数、decltype使用以及数组和指针类型的支持。通过实例展示了如何创建灵活且高效的函数模板,适用于多种数据类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// Using a function template
#include <iostream>
#include <string>
#include <vector>

template<typename T> T larger(T a, T b);    // Function template prototype

// a specialization of the template
template <>
int* larger<int*>(int* a, int* b)
{
	return *a > *b ? a : b;
}


int* larger(int* a, int* b); // Function overloading the larger template

//define a template that overloads the larger() template above

template <typename T>
T larger(const T data[], size_t count)
{
	T result{ data[0] };
	for (size_t i{ 1 }; i < count; ++i)
		if (data[i] > result) result = data[i];
	return result;
}

//define another template overload for vector
template <typename T>
T larger(const std::vector<T>& data)
{
	T result{ data[0] };
	for (auto& value : data)
		if (value > result) result = value;
	return result;
}
// overload the original template with another template specifically for pointer types
template<typename T>
T* larger(T* a, T* b)
{
	return *a > *b ? a : b;
}


int main()
{
	std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl;
	std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl;

	int big_int{ 17011983 }, small_int{ 10 };
	std::cout << "Larger of " << big_int << " and " << small_int << " is "
		<< larger(big_int, small_int) << std::endl;

	//using explicit instantiation of the template
	std::string a_string{ "A" }, z_string{ "Z" };
	std::cout << "Larger of \"" << a_string << "\" and \"" << z_string << "\" is "
		<< '"' << larger(a_string, z_string) << '"' << std::endl;

	/*You put the explicit type argument for the function template between angled brackets after the function
	name.This generates an instance with T as type double.*/
	std::cout << "Larger of " << small_int << " and 19.6 is "
		<< larger<double>(small_int, 19.6) << std::endl; // Outputs 19.6
	std::cout << "Larger of " << small_int << " and 19.6 is "
		<< larger<int>(small_int, 19.6) << std::endl; // Outputs 19
	std::cout << "Larger of " << small_int << " and 19.6 is "
		<< *(larger<int*>(&small_int, &big_int)) << std::endl; // Outputs 17011983
	std::cout << "Larger of " << small_int << " and 19.6 is "
		<< *larger(&small_int, &big_int) << std::endl; // Outputs 17011983

	int data_array[]{ 1,2,3,4,5 };
	std::cout << larger(data_array, std::size(data_array)) << std::endl;//output 5

	const std::vector<int> data_array1{ 1,2,3,4,5 };
	std::cout << larger(data_array1) << std::endl;//output 5

	std::cout << "Larger of " << small_int << " and 19.6 is "
		<< *larger(&small_int, &big_int) << std::endl; // Outputs 17011983

}

// Template for functions to return the larger of two values
template <typename T>
T larger(T a, T b)
{
	return a > b ? a : b;
}

int* larger(int* a, int* b)
{
	return *a > *b ? a : b;
}
#include <iostream>
#include <string>
#include <algorithm>//defines std::max() and std::min()

int main()
{
	int a{ 2 }, b{ 10 };
	std::string str_a{ "A" };
	std::string str_b{ "Z" };

	std::cout << "Larger of int a and b is " << std::max(a, b) << std::endl;
	std::cout << "Smaller of int a and b is " << std::min(a, b) << std::endl;

	std::cout << "Larger of string str_a and str_b is " << std::max(str_a, str_b) << std::endl;
	std::cout << "Smaller of string str_a and str_b is " << std::min(str_a, str_b) << std::endl;
}
// Using decltype() inside a function template
#include <iostream>
#include <vector>
#include <algorithm>	// for std::min()

// Template that computes a so-called "inner product" of two vectors.
// Both vectors are supposed to be equally long, 
// but the function will cope if they aren't.
template<typename T1, typename T2>
auto vector_product(const std::vector<T1>& data1, const std::vector<T2>& data2)
{
	// safeguard against vectors of different sizes
	const auto count = std::min(data1.size(), data2.size());

	decltype(data1[0] * data2[0]) sum{};
	for (size_t i{}; i < count; ++i)
		sum += data1[i] * data2[i];

	return sum;
}

int main()
{
	// Take the product of vectors with different types. Deduced return type: double
	std::vector<int> integers{ 1, 2, 3 };
	std::vector<double> doubles{ 1.1, 1.1, 1.1 };
	std::cout << vector_product(integers, doubles) << std::endl;

	// decltype() does not evaluate the expression, so this will work
	std::vector<bool> empty;
	std::cout << vector_product(empty, empty) << std::endl;
}
// Using decltype() inside a function template
#include <iostream>
/*Using a trailing decltype() syntax, the compiler will deduce the type from the return statements in the function
body.*/
template <typename T1, typename T2>
auto larger(T1 a, T2 b) -> decltype(a > b ? a : b)
{
	return a > b ? a : b;
}

int main()
{
	double num1{ 1.2 };
	int num2{ 3 };
	std::cout << larger(num1, num2) << std::endl;//output 3
}
// Using decltype() inside a function template
#include <iostream>
/*Using this syntax, the compiler will again deduce the type from the return statements in the function
body.*/
template <typename T1, typename T2>
decltype(auto) larger(T1 a, T2 b)
{
	return a > b ? a : b;
}

int main()
{
	double num1{ 1.2 };
	int num2{ 3 };
	std::cout << larger(num1, num2) << std::endl;//output 3
}
// Default Values for Template Parameters
#include <iostream>

template <typename TReturn = int, typename TArg1, typename TArg2>
TReturn larger(const TArg1 a, const TArg2 b)

{
	return a > b ? a : b;
}

int main()
{
	double num1{ 1.2 };
	double num2{ 3.5 };
	std::cout << larger(num1, num2) << std::endl;//output 3

}

// Default Values for Template Parameters
#include <iostream>

template <typename TArg, typename TReturn = TArg>
TReturn larger(const TArg a, const TArg b)

{
	return a > b ? a : b;
}

int main()
{
	double num1{ 1.2 };
	double num2{ 3.5 };
	std::cout << larger(num1, num2) << std::endl;//output 3.5

}
#include <iostream>
/*This template has a type parameter, T, and two nontype parameters, lower and upper, that are both
of type int.*/
template <int lower, int upper, typename T>
bool is_in_range(const T& value)
{
	return (value <= upper) && (value >= lower);
}

int main()
{
	int value{};
	std::cout << "Please enter a number." << std::endl;
	std::cin >> value;
	std::cout << value << (is_in_range<0, 500>(value) ? " is " : " isn't ") << "in the range from 0 to 500" << std::endl; // OK – checks 0 to 500

}
// Defining templates for functions that accept fixed-size arrays
#include <iostream>

template <typename T, size_t N>
T average(const T(&array)[N]);

int main()
{
	double doubles[2]{ 1.0, 2.0 };
	std::cout << average(doubles) << std::endl;

	double moreDoubles[]{ 1.0, 2.0, 3.0, 4.0 };
	std::cout << average(moreDoubles) << std::endl;

	/*Even though arrays and pointers are mostly equivalent,
	the compiler has no way of deducing the array size from a pointer.*/
	// double* pointer = doubles;
	// std::cout << average(pointer) << std::endl;      /* will not compile *

	//passing the brace-enclosed list as an argument.
	std::cout << average({ 1.0, 2.0, 3.0, 4.0 }) << std::endl;

	int ints[] = { 1, 2, 3, 4 };
	std::cout << average(ints) << std::endl;
}

template <typename T, size_t N>
T average(const T(&array)[N])
{
	T sum{};                            // Accumulate total in here
	for (size_t i{}; i < N; ++i)
		sum += array[i];                   // Sum array elements
	return sum / N;                      // Return average
}
// Defining a function template for adding numbers, 
// and an overload that works for pointer types.
// Extra: also make plus() work with string literals...
#include <iostream>
#include <string>
#include <string_view>

template <typename T>
T plus(const T a, const T b)
{
	return a + b;
}

// Overload with another template for pointer types
template <typename T>
T plus(const T* a, const T* b)
{
	return *a + *b;
}

std::string plus(const char* a, const char* b)
{
	return std::string{ a } +b;
}

int main()
{
	int n{ plus(3, 4) };
	std::cout << "plus(3, 4) returns " << n << std::endl;

	double d{ plus(3.2, 4.2) };
	std::cout << "plus(3.2, 4.2) returns " << d << std::endl;

	std::string s1{ "aaa" };
	std::string s2{ "bbb" };
	auto s3 = plus(s1, s2);
	std::cout << "With s1 as " << s1 << " and s2 as " << s2 << std::endl;
	std::cout << "plus(s1, s2) returns " << s3 << std::endl;

	// The extra part:
	std::string s{ plus("he", "llo") };
	std::cout << "plus(\"he\", \"llo\") returns " << s << std::endl;
}
// Your very own interpretation of std::size()
#include <iostream>
#include <array>
#include <vector>

// Look ma, no sizeof()!
template <typename T, size_t N>
inline size_t my_size(const T(&array)[N]) { return N; }

// Overload with two other templates for std::vector<> and array<> 
template <typename T>
inline size_t my_size(const std::vector<T>& vector) { return vector.size(); }

template <typename T, size_t N>
inline size_t my_size(const std::array<T, N>& array) { return N; }  // or array.size();

int main()
{
	int array[]{ 4, 8, 15, 16, 23, 42 };
	std::cout << "Size of numbers is " << my_size(array) << std::endl;//output 6

	// A string literal is also an array:
	std::cout << "Size of life lesson is "
		<< my_size("Always wear a smile. One size fits all.") << std::endl;//output 40

	std::vector<int> vector{ 4, 8, 15, 16, 23, 42 };
	std::cout << "Size of vector is " << my_size(vector) << std::endl;//output 6

	std::array<int, 6> array_object{ 4, 8, 15, 16, 23, 42 };
	std::cout << "Size of array_object is " << my_size(array_object) << std::endl;//output 6
}
	/*check for how many times one specific instance of a function template
	for any given argument type has been called.*/
	// The easiest way to verify this is using a static variable.
	// If indeed each function is only instantiated once, 
	// then all calls should share the same static variable...
	// You should see this being reflected in the program's output
	// as main() starts by calling the function for doubles twice.
#include <iostream>
#include <string>

template<typename T> T larger(T a, T b);    // Function template prototype

int main()
{
	std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl;
	std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl;

	int big_int{ 17011983 }, small_int{ 10 };
	std::cout << "Larger of " << big_int << " and " << small_int << " is "
		<< larger(big_int, small_int) << std::endl;

	std::string a_string{ "A" }, z_string{ "Z" };
	std::cout << "Larger of \"" << a_string << "\" and \"" << z_string << "\" is "
		<< '"' << larger(a_string, z_string) << '"' << std::endl;
}

// Template for functions to return the larger of two values
template <typename T>
T larger(T a, T b)
{

	static size_t counter{};
	std::cout << "This instantation has now been called " << ++counter << " time(s)\n";
	return a > b ? a : b;
}
// Exercise 9-6 A Quicksort funtion template

// The top level sort() template with one parameter calls the sort() template with three parameters
// The sort() function template uses the swap() function template
// The list() template outputs all elements in a vector.

#include <iostream>
#include <iomanip>
#include <vector>

// Swap two vector elements
template<typename T>
inline void swap(std::vector<T>& data, size_t first, size_t second)
{
	T temp{ data[first] };
	data[first] = data[second];
	data[second] = temp;
}

// Sort a range of vector elements
template<typename T>
void sort(std::vector<T>& data, size_t start, size_t end)
{
	// Start index must be less than end index for 2 or more elements
	if (!(start < end))
		return;

	// Choose middle value to partition set
	swap(data, start, (start + end) / 2);         // Swap middle value with start

	// Check data against chosen value
	size_t current{ start };                      // The index of the last element less than the chosen element (after partitioning)
	for (size_t i{ start + 1 }; i <= end; ++i)
	{
		if (data[i] < data[start])                  // Is value less than chosen element?
			swap(data, ++current, i);                 // Yes, so swap to the left
	}

	swap(data, start, current);                   // Swap the chosen value with last in

	if (current) sort(data, start, current - 1);  // Sort left subset if exists
	sort(data, current + 1, end);                 // Sort right subset if exists
}

// Sort all vector elements
template<typename T>
inline void sort(std::vector<T>& values)
{
	if (!values.empty())
		sort(values, 0, values.size() - 1);
}

// Output vector elements
template<typename T>
void list(const std::vector<T>& values, size_t width = 5)
{
	for (auto value : values)
		std::cout << std::setw(width) << value;
	std::cout << std::endl;
}

int main()
{
	std::vector<int> numbers{ -2, 4, -5, 6, 10, -40, 56, 4, 67, 45 };
	list(numbers);
	sort(numbers);
	std::cout << "\nSorted integers:\n";
	list(numbers);

	std::cout << "\nCharacters to be sorted:\n";
	std::vector<char> letters{ 'C', 'd', 'a', 'z', 't', 'S', 'p', 'm', 'D', 'f' };
	list(letters, 2);
	sort(letters);
	std::cout << "\nSorted characters:\n";
	list(letters, 2);

	std::cout << "\nFloating-point values to be sorted:\n";
	std::vector<double> values{ -2.5, 1.4, -2.55, 6.3, 10.1, -40.5, 56.0, 4.7, 67.3, 45.0 };
	list(values, 10);
	sort(values);
	std::cout << "\nSorted floaating-point values:\n";
	list(values, 10);
}

 

Welcome to Beginning C++17. This is a revised and updated version of Ivor Horton’s original book called Beginning ANSI C++. The C++ language has been extended and improved considerably since then, so much so that it was no longer possible to squeeze detailed explanations of all of C++ into a single book. This tutorial will teach the essentials of the C++ language and Standard Library features, which will be more than enough for you to write your own C++ applications. With the knowledge from this book, you should have no difficulty in extending the depth and scope of your C++ expertise. We have assumed no prior programming knowledge. If you are keen to learn and have an aptitude for thinking logically, getting a grip on C++ will be easier than you might imagine. By developing C++ skills, you’ll be learning a language that is already used by millions and that provides the capability for application development in just about any context. C++ is very powerful. Arguably, it’s more powerful than most programming languages. So, yes, like with any powerful tool you can wield some considerable damage if you use it without proper training. We often compare C++ to a Swiss Army knife: age-old, trusted, incredibly versatile, yet potentially mind-boggling and full of pointy things that could really hurt you. Once someone clearly explains to you what all the different tools are meant for, however, and teaches you some elementary knife safety rules, then you’ll never have to look for another pocketknife again. C++ does not need to be dangerous or difficult at all either. C++ today is much more accessible than many people assume. The language has come a long way since its conception nearly 40 years ago. In essence, we have learned how to wield all its mighty blades and tools in the safest and most effective way possible. And, more importantly perhaps, the C++ language and its Standard Library have evolved accordingly to facilitate this. The past decade in particular has seen the ris
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值