掌握C++模板的艺术:类型参数、默认值和自动推导
一、模板参数
1.类型模板参数
在 Grid 示例中,Grid 模板有一个模板参数:存储在网格中的类型。编写类模板时,您需要在尖括号内指定参数列表,例如:
template
这个参数列表类似于函数或方法中的参数列表。与函数和方法一样,你可以编写具有任意多个模板参数的类。此外,这些参数不必是类型,它们可以有默认值。
2.非类型模板参数
非类型参数是普通参数,如整数和指针——这类参数你可能已经在函数和方法中很熟悉了。然而,非类型模板参数只能是整型(char、int、long 等)、枚举类型、指针、引用、std::nullptr_t、auto、auto& 和 auto*。C++20 还允许浮点类型和类类型的非类型模板参数。后者有很多限制,在本文中不再详细讨论。
在 Grid 类模板中,你可以使用非类型模板参数来指定网格的高度和宽度,而不是在构造函数中指定。在模板列表中指定非类型参数而不是在构造函数中指定的主要优点是这些值在代码编译之前就已知。回想一下,编译器通过在编译之前替换模板参数来生成模板实例的代码。因此,你可以在实现中使用普通的二维数组,而不是动态调整大小的向量数组。以下是带有更改的新类定义:
export template class Grid { public: Grid() = default; virtual ~Grid() = default; // 明确默认复制构造函数和赋值运算符。 Grid(const Grid& src) = default; Grid& operator=(const Grid& rhs) = default; std::optional& at(size_t x, size_t y); const std::optional& at(size_t x, size_t y) const; size_t getHeight() const { return HEIGHT; } size_t getWidth() const { return WIDTH; } private: void verifyCoordinate(size_t x, size_t y) const; std::optional m_cells[WIDTH][HEIGHT]; };
注意,模板参数列表需要三个参数:存储在网格中的对象类型,以及网格的宽度和高度。宽度和高度用于创建存储对象的二维数组。下面是类方法的定义:
// 类方法定义 template void Grid::verifyCoordinate(size_t x, size_t y) const { if (x >= WIDTH) { throw std::out_of_range { std::format("{} must be less than {}.", x, WIDTH) }; } if (y >= HEIGHT) { throw std::out_of_range { std::format("{} must be less than {}.", y, HEIGHT) }; } } template const std::optional& Grid::at(size_t x, size_t y) const { verifyCoordinate(x, y); return m_cells[x][y]; } template std::optional& Grid::at(size_t x, size_t y) { return const_cast(std::as_const(*this).at(x, y)); }
注意,之前你在哪里指定了 Grid,现在你必须指定 Grid 来指定三个模板参数。你可以这样实例化并使用这个模板:
Grid myGrid; Grid anotherGrid; myGrid.at(2, 3) = 42; anotherGrid = myGrid; cout SpreadsheetCell;
这个指南必须在类定义之外但在与 SpreadsheetCell 类相同的命名空间内定义。通用语法如下。explicit 关键字是可选的,其行为与构造函数的 explicit 相同。通常,这样的推导指南也是模板。
explicit TemplateName(Parameters) -> DeducedTemplate;