嵌套模板如何在 C++ 中解析?
How do nested templates get resolved in C++?
我最近问了一个关于在编译时确定迭代器是否指向复数值的问题,并得到了一个有效的答案。
问题在这里:
解决方案是一组模板,用于确定一个模板是否是另一个模板的特化:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
这确实有效,但我真的很难理解它是如何工作的。特别是 template
中的嵌套 template
让我感到困惑。我对使用可变参数模板还很陌生,使用没有提供类型的可变参数模板似乎很奇怪,例如:<class...>
而不是这样的 <class... Args>
.
有人可以分解这个模板并描述它是如何解决的吗?
你要数一数模板参数的三种类型:
1) 类型
2) 非类型(或值)
3) 模板模板
第一种前面有typename
(或class
)
template <typename T>
void foo (T const & t);
在前面的示例中,T
是一个类型,t
(一个 class 函数参数)是一个 T
.[=48= 类型的值]
第二类模板参数是值,前面是值的类型(或 auto
,从 C++17 开始,对于未指定的类型)
template <int I>
void bar ()
{ std::cout << I << std::endl; }
在前面的示例中,I
模板参数是 int
.
类型的值
第三种解释起来最复杂
你知道(我想)std::vector<int>
和 std::vector<double>
是不同的类型,但它们有共同点 std::vector
,一个模板 class.
模板模板参数是接受 std::vector
的参数,模板 class 不带参数。
模板模板参数前面有一个template
关键字,如下例
template <template <int> class C>
void baz ();
前面示例中的模板模板参数 C
是需要单个 int
(值)模板参数的 class(或结构)。
所以如果你有 class
template <int I>
class getInt
{ };
您可以将 getInt
作为模板参数传递给 baz()
baz<getInt>();
现在您应该能够理解您的代码了:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
is_specialization
结构是一个模板结构,作为模板参数接收一个类型 (T
) 和一个接受 classes/structs 接收的模板模板 Template
可变数量的类型模板参数。
现在你的专长是is_specialization
:
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
当第一个模板参数 (Template<Args...>
) 是基于第二个模板参数 (Template
) 的 class 时,选择此专业化。
一个例子:如果你实例化
is_specialization<std::vector<int>, std::map>
选择主要版本(继承自 std::false_type
)因为 std::vector<int>
不是基于 std::map
。
但是如果你实例化
is_specialization<std::vector<int>, std::vector>
选择专业化(从 std::true_type
继承)是因为 std::vector<int>
基于 std::vector
。
这是一个基于导致我脑海中出现“咔嗒”声的原因的答案,我相信如果您能遵循 max66 答案,它会更有用。如果你喜欢我,即使在阅读了这篇文章之后你仍然在这里挣扎,这是我试图解释的。
不要阅读模板的第一行
所以这个:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
变成这样:
struct is_specialization : std::false_type {};
struct is_specialization<Template<Args...>, Template> : std::true_type {};
现在事情变得更简单了。我们有第一个一般情况和第二个特殊情况(编译器在可以匹配参数时首选)。
我说的匹配不是什么花哨的,几乎是字面的文本匹配。
例如,如果我们这样做
is_specialization<std::vector<double>, std::set>
现在编译器将尝试匹配特殊情况。
你可以想象它从字面上用 std::vector
替换 Template
(如果他首先匹配第一个参数),然后失败,因为他现在期望 Template
意味着 std::vector
和在第二个参数中它是 std::set
。
他还将 Args...
替换为 double
但这并不重要,因为这不是不匹配的原因。
类似地,编译器可能首先尝试匹配第二个参数并得出结论 Template
必须是 std::set
,然后他失败了,因为第一个参数不是 std::set<Args...>
.
我认为模板的第一行不那么重要并且更容易理解,但为了完整起见,让我们回顾一下。
template <class T, template <class...> class Template>
只是表示这是一个模板,其第二个参数是模板模板参数,这是必需的,因此您可以将 std::set
之类的内容作为第二个参数传递(请注意,通常这不起作用,您需要传递某种类型的 std::set
,例如 std::set<float>
).
最丑的部分是第二个模板的第一行:
template <template <class...> class Template, class... Args>
如果我们还记得我们在专业化代码中所做的事情,这里又是有意义的。
我们有模板模板参数 Template
,并且需要 Args
只是因为我们想使用模板模板参数作为普通模板参数(作为第一个参数(Template<Args...>
))。
tl;dr
忽略模板的第一行,模式匹配。
免责声明:就像我说的,这只是我向自己解释这段代码的方式,我知道普通人读到“第一行”或“模式匹配”这样的短语时会哭。
我最近问了一个关于在编译时确定迭代器是否指向复数值的问题,并得到了一个有效的答案。
问题在这里:
解决方案是一组模板,用于确定一个模板是否是另一个模板的特化:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
这确实有效,但我真的很难理解它是如何工作的。特别是 template
中的嵌套 template
让我感到困惑。我对使用可变参数模板还很陌生,使用没有提供类型的可变参数模板似乎很奇怪,例如:<class...>
而不是这样的 <class... Args>
.
有人可以分解这个模板并描述它是如何解决的吗?
你要数一数模板参数的三种类型:
1) 类型
2) 非类型(或值)
3) 模板模板
第一种前面有typename
(或class
)
template <typename T>
void foo (T const & t);
在前面的示例中,T
是一个类型,t
(一个 class 函数参数)是一个 T
.[=48= 类型的值]
第二类模板参数是值,前面是值的类型(或 auto
,从 C++17 开始,对于未指定的类型)
template <int I>
void bar ()
{ std::cout << I << std::endl; }
在前面的示例中,I
模板参数是 int
.
第三种解释起来最复杂
你知道(我想)std::vector<int>
和 std::vector<double>
是不同的类型,但它们有共同点 std::vector
,一个模板 class.
模板模板参数是接受 std::vector
的参数,模板 class 不带参数。
模板模板参数前面有一个template
关键字,如下例
template <template <int> class C>
void baz ();
前面示例中的模板模板参数 C
是需要单个 int
(值)模板参数的 class(或结构)。
所以如果你有 class
template <int I>
class getInt
{ };
您可以将 getInt
作为模板参数传递给 baz()
baz<getInt>();
现在您应该能够理解您的代码了:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
is_specialization
结构是一个模板结构,作为模板参数接收一个类型 (T
) 和一个接受 classes/structs 接收的模板模板 Template
可变数量的类型模板参数。
现在你的专长是is_specialization
:
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
当第一个模板参数 (Template<Args...>
) 是基于第二个模板参数 (Template
) 的 class 时,选择此专业化。
一个例子:如果你实例化
is_specialization<std::vector<int>, std::map>
选择主要版本(继承自 std::false_type
)因为 std::vector<int>
不是基于 std::map
。
但是如果你实例化
is_specialization<std::vector<int>, std::vector>
选择专业化(从 std::true_type
继承)是因为 std::vector<int>
基于 std::vector
。
这是一个基于导致我脑海中出现“咔嗒”声的原因的答案,我相信如果您能遵循 max66 答案,它会更有用。如果你喜欢我,即使在阅读了这篇文章之后你仍然在这里挣扎,这是我试图解释的。
不要阅读模板的第一行
所以这个:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
变成这样:
struct is_specialization : std::false_type {};
struct is_specialization<Template<Args...>, Template> : std::true_type {};
现在事情变得更简单了。我们有第一个一般情况和第二个特殊情况(编译器在可以匹配参数时首选)。 我说的匹配不是什么花哨的,几乎是字面的文本匹配。
例如,如果我们这样做
is_specialization<std::vector<double>, std::set>
现在编译器将尝试匹配特殊情况。
你可以想象它从字面上用 std::vector
替换 Template
(如果他首先匹配第一个参数),然后失败,因为他现在期望 Template
意味着 std::vector
和在第二个参数中它是 std::set
。
他还将 Args...
替换为 double
但这并不重要,因为这不是不匹配的原因。
类似地,编译器可能首先尝试匹配第二个参数并得出结论 Template
必须是 std::set
,然后他失败了,因为第一个参数不是 std::set<Args...>
.
我认为模板的第一行不那么重要并且更容易理解,但为了完整起见,让我们回顾一下。
template <class T, template <class...> class Template>
只是表示这是一个模板,其第二个参数是模板模板参数,这是必需的,因此您可以将 std::set
之类的内容作为第二个参数传递(请注意,通常这不起作用,您需要传递某种类型的 std::set
,例如 std::set<float>
).
最丑的部分是第二个模板的第一行:
template <template <class...> class Template, class... Args>
如果我们还记得我们在专业化代码中所做的事情,这里又是有意义的。
我们有模板模板参数 Template
,并且需要 Args
只是因为我们想使用模板模板参数作为普通模板参数(作为第一个参数(Template<Args...>
))。
tl;dr
忽略模板的第一行,模式匹配。
免责声明:就像我说的,这只是我向自己解释这段代码的方式,我知道普通人读到“第一行”或“模式匹配”这样的短语时会哭。