当前位置: 首页 > article >正文

C++学习笔记----11、模块、头文件及各种主题(一)---- 模板概览与类模板(7)

2.6、类模板特殊化

        对于特定类型可以提供另外的类模板的实现。例如,你可能决定Grid的const char*(C风格的字符串)的行为是讲不通的。Grid<const char*>会保存其元素在vector<optional<const char*>>中。拷贝构造函数与赋值操作符会执行该const char*指针的空拷贝。对于const char*,进行字符串的深度拷贝是讲得通的。最简单的解决方案是特别为const char*写另外一个实现,它将char *转变为C++ string并且保存在vector<optional<string>>中。

        另外一种模板的实现叫模板特殊化。一开始可能会发现语法有一点儿怪怪的。当写一个类模板特殊化时,必须指定它是基于模板的,并且是在写一个特定类型的一个模板的版本。下面是Grid的const char*特殊化的语法。在这个实现中,原来的Grid类模板被移到了一个叫做main的模块接口分区中,而特殊化在一个叫做string的模块接口分区中。

export module grid:string;

import std;

// When the template specialization is used, the original template must be
// visible too.
import :main;

export
template <>
class Grid<const char*>
{
public:
	explicit Grid(std::size_t width = DefaultWidth, std::size_t height = DefaultHeight);
	virtual ~Grid() = default;

	// Explicitly default a copy constructor and copy assignment operator.
	Grid(const Grid& src) = default;
	Grid& operator=(const Grid& rhs) = default;

	// Explicitly default a move constructor and move assignment operator.
	Grid(Grid&& src) = default;
	Grid& operator=(Grid&& rhs) = default;

	std::optional<std::string>& at(std::size_t x, std::size_t y);
	const std::optional<std::string>& at(std::size_t x, std::size_t y) const;

	std::size_t getHeight() const { return m_height; }
	std::size_t getWidth() const { return m_width; }

	static constexpr std::size_t DefaultWidth{ 10 };
	static constexpr std::size_t DefaultHeight{ 10 };

private:
	void verifyCoordinate(std::size_t x, std::size_t y) const;

	std::vector<std::optional<std::string>> m_cells;
	std::size_t m_width { 0 }, m_height { 0 };
};

        注意不用指向任何变量类型,比如T,在特殊化中:直接在const char*与string上工作。此时一个明显的问题是为什么这个类仍然有一个模板头。也就是说,下面的语法有什么好处?

template <>
class Grid<const char*>

        该语法告诉编译器这个类是一个const char*特殊化的Grid类模板。假定你不用这个语法,尝试这样写:

class Grid

        编译器不会让你这么做的,因为已经有一个类模板叫Grid了(原来的类模板)。只有特殊化它才能重用这个名字。特殊化的主要益处是对用户是不可见的。当用户生成一个int或SpreadsheetCell的Grid时,编译器从原来的Grid模板生成代码。当用户生成const char*的Grid时,编译器使用const char*特殊化。这些都是在幕后完成的。

        主模块接口文件只是导入与导出两个模块接口分区:

export module grid;
export import :main;
export import :string;

        特殊化可以测试如下:

	Grid<int> myIntGrid;					// Uses original Grid template.
	Grid<const char*> stringGrid1{ 2, 2 };	// Uses const char* specialization.

	const char* dummy{ "dummy" };
	stringGrid1.at(0, 0) = "hello";
	stringGrid1.at(0, 1) = dummy;
	stringGrid1.at(1, 0) = dummy;
	stringGrid1.at(1, 1) = "there";

	Grid<const char*> stringGrid2{ stringGrid1 };

        当特殊化一个类模板时,没有“继承”任何代码;特殊化不像派生。必须重写类的整个实现。不要求提供同样名字或行为的成员函数。作为一个例子,const char*特殊化的Grid实现at()成员函数返回optional<string>,而不是optional<const char*>。实际上,可以写一个与原来无关的完全不同的类。当然了,那是对模板特殊化能力的滥用,没有正当理由不要这么干。下面是const char*特殊化的成员函数的实现。与类模板定义不同,不用重复模板头,template<>在每个成员函数定义的前面。

Grid<const char*>::Grid(std::size_t width, std::size_t height)
	: m_width{ width }
	, m_height{ height }
{
	m_cells.resize(m_width * m_height);
}

void Grid<const char*>::verifyCoordinate(std::size_t x, std::size_t y) const
{
	if (x >= m_width) {
		throw std::out_of_range { std::format("x ({}) must be less than width ({}).", x, m_width) };
	}
	if (y >= m_height) {
		throw std::out_of_range { std::format("y ({}) must be less than height ({}).", y, m_height) };
	}
}

const std::optional<std::string>& Grid<const char*>::at(std::size_t x, std::size_t y) const
{
	verifyCoordinate(x, y);
	return m_cells[x + y * m_width];
}

std::optional<std::string>& Grid<const char*>::at(std::size_t x, std::size_t y)
{
	return const_cast<std::optional<std::string>&>(std::as_const(*this).at(x, y));
}

        本节讨论如何使用类模板特殊化来书写类模板的特殊实现,用特定类型替换模板类型参数。这叫做完整模板特殊化。这样的完整类模板特殊化就不再是类模板自身了,而是一个类定义。后面的章节会继续讨论类模板特殊化,有更高级的特性叫做部分特殊化。


http://www.kler.cn/a/391076.html

相关文章:

  • 冗余连接2 hard题 代随C#写法
  • macOS解决U盘装完系统容量变小的问题
  • K8s进阶使用
  • gdb编译教程(支持linux下X86和ARM架构)
  • IEC60870-5-104 协议源码架构详细分析
  • 第三十六章 Vue之路由重定向/404页面设置/路径模式设置
  • 设计模式之单列模式(7种单例模式案例,Effective Java 作者推荐枚举单例模式)
  • 城镇住房保障:SpringBoot系统架构解析
  • 科技前沿:汽车智能玻璃,开启透明显示新纪元
  • 【二叉树】——
  • 人保财险(外包)面试分享
  • UI资源分包 -- 基于Xasset框架代码实例
  • Ubuntu中以root身份运行Qt创建的项目
  • UML概述、类图关系及连接线表示
  • 【MQTT】代理服务比较RabbitMQ、Mosquitto 和 EMQX
  • MySQ怎么使用语法介绍(详细)
  • 工业主板在汽车制造中的应用
  • php 如何将数组转成对象数组
  • 人工智能(10)——————自然语言处理
  • 网络安全管理与运维服务_网络安全运维方案
  • HCIP—快速生成树协议(RSTP)实验配置
  • 剪辑视频和制作视频的软件哪个好
  • A018基于Spring Boot的民宿租赁系统
  • 2024年华为OD机试真题-关联子串-Java-OD统一考试(E卷)
  • 使用 PageHelper 在 Spring Boot 项目中实现分页查询
  • 跨子网的WinCC客户机/服务器如何实现通讯?