掌握C++模板的艺术:类型参数、默认值和自动推导( 二 )

然而,如果你将高度定义为常量 , 则可以编译:
const size_t height { 10 };Grid<int, 10, height> testGrid; // 可编译并工作具有正确返回类型的 constexpr 函数也可以工作 。例如,如果你有一个返回 size_t 的 constexpr 函数,你可以用它来初始化高度模板参数:
constexpr size_t getHeight() { return 10; }...Grid<double, 2, getHeight()> myDoubleGrid;第二个限制可能更重要 。现在宽度和高度是模板参数,它们是每个网格类型的一部分 。这意味着 Grid<int,10,10> 和 Grid<int,10,11> 是两种不同的类型 。你不能将一种类型的对象赋值给另一种类型的对象,也不能将一种类型的变量传递给期望另一种类型变量的函数或方法 。

注意:非类型模板参数成为实例化对象类型规范的一部分 。
类模板参数的默认值设置高度和宽度的默认值如果您继续使用高度和宽度作为模板参数的方法,您可能想为 Grid<T> 类构造函数中之前的高度和宽度非类型模板参数提供默认值 。C++ 允许您使用类似的语法为模板参数提供默认值 。同时,您也可以为 T 类型参数提供默认值 。下面是类定义:
export template <typename T = int, size_t WIDTH = 10, size_t HEIGHT = 10>class Grid {    // 其余部分与之前版本相同};在方法定义的模板规范中,您不需要为 TWIDTH 和 HEIGHT 指定默认值 。例如,这是 at() 方法的实现:
template <typename T, size_t WIDTH, size_t HEIGHT>const std::optional<T>& Grid<T, WIDTH, HEIGHT>::at(size_t x, size_t y) const {    verifyCoordinate(x, y);    return m_cells[x][y];}现在,您可以在没有任何模板参数的情况下实例化 Grid,只需指定元素类型,元素类型和宽度 , 或元素类型、宽度和高度:
Grid<> myIntGrid;Grid<int> myGrid;Grid<int, 5> anotherGrid;Grid<int, 5, 5> aFourthGrid;请注意 , 如果您不指定任何类模板参数,您仍然需要指定一组空的尖括号 。例如,以下代码无法编译!
Grid myIntGrid;类模板参数列表中默认参数的规则与函数或方法相同;也就是说,您可以从右边开始为参数提供默认值 。
类模板参数推导(CTAD)自动推导模板参数类模板参数推导允许编译器自动从传递给类模板构造函数的参数推导出模板参数 。例如,标准库中有一个名为 std::pAIr 的类模板,在 <utility> 中定义 , 并在第1章中介绍 。pair 存储两个可能不同类型的值,通常需要指定为模板参数 。例如:
pair<int, double> pair1 { 1, 2.3 };为了避免编写模板参数,可以使用一个名为 std::make_pair() 的辅助函数模板 。编写自己的函数模板的细节将在本章后面讨论 。函数模板一直支持基于传递给函数模板的参数自动推导模板参数 。因此,make_pair() 能够根据传递给它的值自动推导出模板类型参数 。例如 , 编译器为以下调用推导出 pair<int, double>
auto pair2 { make_pair(1, 2.3) };使用类模板参数推导(CTAD),不再需要这样的辅助函数模板 。编译器现在会根据传递给构造函数的参数自动推导出模板类型参数 。对于 pair 类模板,您可以简单地编写以下代码:
pair pair3 { 1, 2.3 }; // pair3 的类型为 pair<int, double>当然,这仅在类模板的所有模板参数要么具有默认值,要么用作构造函数中的参数,从而可以推导出来时才有效 。请注意,CTAD 要求有一个初始化器才能工作 。以下是非法的:
pair pair4;许多标准库类支持 CTAD,例如 vectorarray 等 。
注意:这种类型推导对 


推荐阅读