26-x 第26章总结和测验

模板允许我们使用占位符类型编写函数或类,这样我们就可以使用不同的类型创建相同版本的函数或类。已被实例化的函数或类称为函数或类实例。

所有模板函数或类都必须以模板参数声明开头,该声明告诉编译器后面的函数或类是模板函数或类。在模板参数声明中,指定模板类型参数或表达式参数。模板类型参数只是占位符类型,通常命名为 T、T1、T2 或其他单字母名称(例如 S)。表达式参数通常是整型,但也可以是指向函数、类对象或成员函数的指针或引用。

将模板类定义和成员函数定义分开处理与普通类的处理方式不同——你不能把类定义放在头文件中,把成员函数定义放在 .cpp 文件中。通常最好将它们全部放在一个头文件中,并将成员函数定义放在类定义下方。

当我们想要针对特定​​类型覆盖模板函数或类的默认行为时,可以使用模板特化。如果所有类型都被覆盖,则称为完全特化。类也支持部分特化,即只对部分模板参数进行特化。函数不能进行部分特化。

C++标准库中的许多类都使用了模板,包括std::array和std::vector。模板通常用于实现容器类,这样容器只需编写一次,即可用于任何合适的类型。

测试时间

1.有时需要定义成对传输的数据。编写一个名为 Pair1 的模板类,允许用户定义一个模板类型,该类型将用于一对数据中的两个值。以下函数应该可以正常工作:

int main()
{
	Pair1<int> p1 { 5, 8 };
	std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n';

	const Pair1<double> p2 { 2.3, 4.5 };
	std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n';

	return 0;
}

并打印:

img

解决方案:

#include <iostream>

template <typename T>
class Pair1
{
private:
	T m_x {};
	T m_y {};

public:
	Pair1(const T& x, const T& y)
		: m_x{ x }, m_y{ y }
	{
	}

	T& first() { return m_x; }
	T& second() { return m_y; }
	const T& first() const { return m_x; }
	const T& second() const { return m_y; }
};

int main()
{
	Pair1<int> p1 { 5, 8 };
	std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n';

	const Pair1<double> p2 { 2.3, 4.5 };
	std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n';

	return 0;
}

2.编写一个 Pair 类,允许你为 Pair 中的两个值分别指定不同的类型。
注意:我们给这个类命名的方式与之前的类不同,因为 C++ 目前不允许您“重载”仅在模板参数的数量或类型上有所不同的类。

以下程序应该可以运行:

int main()
{
	Pair<int, double> p1 { 5, 6.7 };
	std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n';

	const Pair<double, int> p2 { 2.3, 4 };
	std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n';

	return 0;
}

并打印:

img

提示:要使用两种不同类型的模板定义模板,请在模板参数声明中用逗号分隔这两种类型。更多信息请参见第11.8 课——具有多种模板类型的函数模板。
解决方案:

#include <iostream>

template <typename T, typename S>
class Pair
{
private:
	T m_x;
	S m_y;

public:
	Pair(const T& x, const S& y)
		: m_x{x}, m_y{y}
	{
	}

	T& first() { return m_x; }
	S& second() { return m_y; }
	const T& first() const { return m_x; }
	const S& second() const { return m_y; }
};

int main()
{
	Pair<int, double> p1 { 5, 6.7 };
	std::cout << "Pair: " << p1.first() << ' ' << p1.second() << '\n';

	const Pair<double, int> p2 { 2.3, 4 };
	std::cout << "Pair: " << p2.first() << ' ' << p2.second() << '\n';

	return 0;
}

3.字符串值对是一种特殊的键值对,其中第一个值始终是字符串类型,第二个值可以是任何类型。编写一个名为 StringValuePair 的模板类,该类继承自一个部分特化的 Pair 类(使用 std::string 作为第一个类型,并允许用户指定第二个类型)。
应该运行以下程序:

int main()
{
	StringValuePair<int> svp { "Hello", 5 };
	std::cout << "Pair: " << svp.first() << ' ' << svp.second() << '\n';

	return 0;
}

并打印:

img

提示:从 StringValuePair 构造函数调用 Pair 构造函数时,不要忘记将模板参数作为 Pair 类名的一部分。

解决方案:

#include <iostream>
#include <string>
#include <string_view>

template <typename T, typename S>
class Pair
{
private:
	T m_x{};
	S m_y{};

public:
	Pair(const T& x, const S& y)
		: m_x{ x }, m_y{ y }
	{
	}

	T& first() { return m_x; }
	S& second() { return m_y; }
	const T& first() const { return m_x; }
	const S& second() const { return m_y; }
};

template <typename S>
class StringValuePair : public Pair<std::string, S>
{
public:
	StringValuePair(std::string_view key, const S& value)
                // a std::string_view won't implicitly convert to a std::string, we must be explicit
		: Pair<std::string, S>{ static_cast<std::string>(key), value }
	{
	}
};

int main()
{
	StringValuePair<int> svp{ "Hello", 5 };
	std::cout << "Pair: " << svp.first() << ' ' << svp.second() << '\n';

	return 0;
}
posted @ 2025-11-30 02:03  游翔  阅读(8)  评论(0)    收藏  举报