10.11 - 类模板参数推断CTAD
Key Takeaway
类模板实参推断 (CTAD) (C++17)
从C++ 17开始,当从类模板实例化一个对象时,编译器可以从对象的初始化式的类型推断出模板类型(类模板实参推断,简称CTAD)。例如:
1 2 3 4 5 6 7 8 9 |
|
CTAD 只有在类模板列表中没有提供任何参数时才会进行,因此下面代码中的两种方式都是错误的:
1 2 3 4 5 6 7 8 9 |
|
作者注
本网站今后的许多课程都利用了CTAD。如果使用C++14标准编译这些示例,将会得到一个关于缺少模板参数的错误。您需要显式地将这些参数添加到示例中,以使其能够编译。
模板参数推断指南 (C++17)
在大多数情况下,CTAD可以开箱即用。然而,在某些情况下,编译器可能需要一些额外的帮助,以理解如何正确地推导模板实参。
你可能会惊讶地发现下面的程序(它几乎与上面使用std::pair
的例子相同)不能在C++ 17中编译:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
如果你在C++17中编译它,你可能会得到一些关于“类模板实参推导失败”或“无法推导模板实参”或“没有可行的构造函数或推导指南”的错误。这是因为在C++17中,CTAD不知道如何推导聚合类模板的模板实参。为了解决这个问题,我们可以为编译器提供指南,告诉编译器如何推断给定类模板的模板实参。
下面的程序为编译器提供了推断指南:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
上面的例子可以在C++17中成功编译。
此处Pair
类的类型推断指南很简单,让我们仔细研究一下它是如何工作的吧。
1 2 3 4 |
|
首先,我们需要使用和 Pair
类一样的模板定义,因为推断定义的目的就是告诉编译器如何推断 Pair<T, U>
类型。接下来,在箭头符号的右侧,我们提供了用于帮助编译器进行推断的类型。在这个例子中,我们希望编译器能够为 Pair<T, U>
类型的对象进行模板类型推断。最后,在箭头符号的左侧,我们告诉编译器应该关注什么样的声明。在这个例子中,编译器被要求关注名为 Pair
且有两个形参(一个是 T
类型,一个是U
类型)的声明。这里也可以写作 Pair(T t, U u)
( t
和 u
是参数的名字,但是因为我们不需要使用t
和 u
,所以也没必要给它们一个名字)。
上述操作综合起来,告诉编译器如果看到有一个 Pair
类型的对象,且有两个实参,则应该将其推断为类型 Pair<T, U>
。
所以当编译器看到 Pair p2{ 1, 2 };
时,它会说:“噢,这是一个Pair
对象,有两个实参int
和int
,所以基于推断指南,我应该将其推断为类型Pair<int, int>
” 。
下面是一个接受单一模板类型的Pair
的类似示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
在这个例子中,类型推断指南将 Pair(T, T)
(Pair
类型且有两个类型为T的参数 T
) 推断为类型 Pair<T>
。
作者注
关于类型推断指南的一些备注。
首先,std::pair
(以及其他标准库模板类型)是有预定义的类型推断指南的。这也是为什么使用 std::pair
的例子是可以在 C++17 中直接编译的。
其次,C++20 为编译器提供了为聚合类型自动生成类型推断指南的能力,因此前面使用Pair
的例子可以在C++20编译器中编译。这里假设你的编译器支持P1816特性,在编写本文时,gcc和Visual Studio支持P1816特性,而Clang不支持。