C++类模板特化与继承使用说明书,新手也能get

一、类模板特化1.特化的实现你可以为特定类型提供类模板的替代实现 。例如,你可能认为 const char 类型(C 风格字符串)的 Grid 行为没有意义 。Grid<const char> 将在 vector<vector<optional<const char*>>> 中存储其元素 。拷贝构造函数和赋值运算符将执行这些 const char 指针类型的浅拷贝 。对于 const char,进行深拷贝字符串可能更有意义 。最简单的解决方案是为 const char 编写一个专门的实现,将它们转换为 C++ 字符串,并存储在 vector<vector<optional<string>>> 中 。

C++类模板特化与继承使用说明书,新手也能get

文章插图
【C++类模板特化与继承使用说明书,新手也能get】模板的替代实现称为模板特化 。你可能会发现其语法初看有些奇怪 。当你编写类模板特化时,你必须指定这是模板,并且你正在为特定类型编写模板的版本 。以下是 Grid 的 const char
export module grid:string;// 当使用模板特化时,原始模板也必须可见 。import :mAIn;export template <>class Grid<const char*> {public:explicit Grid(size_t width = DefaultWidth, size_t height = DefaultHeight);virtual ~Grid() = default;// 明确默认拷贝构造函数和赋值运算符 。Grid(const Grid& src) = default;Grid& operator=(const Grid& rhs) = default;// 明确默认移动构造函数和赋值运算符 。Grid(Grid&& src) = default;Grid& operator=(Grid&& rhs) = default;std::optional<std::string>& at(size_t x, size_t y);const std::optional<std::string>& at(size_t x, size_t y) const;size_t getHeight() const { return m_height; }size_t getWidth() const { return m_width; }static const size_t DefaultWidth { 10 };static const size_t DefaultHeight { 10 };private:void verifyCoordinate(size_t x, size_t y) const;std::vector<std::vector<std::optional<std::string>>> m_cells;size_t m_width { 0 }, m_height { 0 };};注意 , 在特化中你不使用任何类型变量,例如 T,你直接使用 const char* 和字符串 。此时一个明显的问题是 , 为什么这个类仍然是模板 。即 , 以下语法有什么用途?
template <> class Grid<const char*>这种语法告诉编译器,这个类是 Grid 类的 const char* 特化 。假设你没有使用这种语法,而是尝试编写如下代码:
class Grid编译器不会允许你这样做 , 因为已经存在一个名为 Grid 的类模板(原始类模板) 。只有通过特化,你才能重用这个名称 。特化的主要好处是它们对用户来说可以是不可见的 。当用户创建 int 或 SpreadsheetCells 的 Grid 时,编译器会从原始 Grid 模板生成代码 。当用户创建 const char* 的 Grid 时,编译器使用 const char* 特化 。这一切都可以在“幕后”进行 。
2.主模块接口文件主模块接口文件简单地导入并导出两个模块接口分区:
export module grid;export import :main;export import :string;特化可以按照以下方式进行测试:
Grid<int> myIntGrid; // 使用原始 Grid 模板 。Grid<const char*> stringGrid1 { 2, 2 }; // 使用 const char* 特化 。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(size_t width, size_t height): m_width { width }, m_height { height } {m_cells.resize(m_width);for (auto& column : m_cells) {column.resize(m_height);}}void Grid<const char*>::verifyCoordinate(size_t x, size_t y) const {if (x >= m_width) {throw std::out_of_range { std::format("{} must be less than {}.", x, m_width) };}if (y >= m_height) {throw std::out_of_range { std::format("{} must be less than {}.", y, m_height) };}}const std::optional<std::string>& Grid<const char*>::at(size_t x, size_t y) const {verifyCoordinate(x, y);return m_cells[x][y];}std::optional<std::string>& Grid<const char*>::at(size_t x, size_t y) {return const_cast<std::optional<std::string>&>(std::as_const(*this).at(x, y));}


推荐阅读