【问题标题】:How do nested templates get resolved in C++?如何在 C++ 中解析嵌套模板?
【发布时间】:2020-05-15 11:32:19
【问题描述】:

我最近问了一个关于在编译时确定迭代器是否指向复杂值的问题,并得到了有效的答案。

问题在这里: How can I specialize an algorithm for iterators that point to complex values?

解决方案是使用一组模板来确定一个模板是否是另一个模板的特化:

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 让我感到困惑。我对使用可变参数模板还是很陌生,拥有一个没有提供类型的可变参数模板似乎很奇怪,例如:&lt;class...&gt; 而不是像 &lt;class... Args&gt; 这样的东西。

有人可以分解这个模板并描述它是如何解决的吗?

【问题讨论】:

  • 请每个问题回答一个问题。第一次见这里:stackoverflow.com/questions/213761/…
  • @foreknownas_463035818 我已经阅读了该帖子中的答案,但似乎没有一个清楚地描述它是如何工作的。他们确实展示了一些用例,但在不知道它是如何工作的情况下,没有一个用例对我来说真正有意义。

标签: c++ templates variadic-templates template-meta-programming template-templates


【解决方案1】:

您必须考虑到模板参数的三种类型:

1) 类型

2) 非类型(或值)

3) 模板模板

第一种前面是typename(或class

template <typename T>
void foo (T const & t);

在前面的示例中,T 是一个类型,t(一个经典函数参数)是一个 T 类型的值。

模板参数的第二种类型是值,前面是值的类型(或 auto,从 C++17 开始,用于未指定类型)

template <int I>
void bar ()
 { std::cout << I << std::endl; }

在前面的示例中,I 模板参数是 int 类型的值。

第三种是最难解释的。

你知道(我猜想)std::vector&lt;int&gt;std::vector&lt;double&gt; 是不同的类型,但它们有一个共同点 std::vector,一个模板类。

模板模板参数是接受std::vector的参数,模板类不带参数。

模板模板参数前面有一个template 关键字,如下例所示

template <template <int> class C>
void baz ();

上例中的模板模板参数C 是需要单个int(值)模板参数的类(或结构)。

如果你有课

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) 和一个模板模板 Template,它接受接收可变数量的类型模板参数的类/结构。

现在你的专业化是is_specialization

template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};

当第一个模板参数 (Template&lt;Args...&gt;) 是基于第二个模板参数 (Template) 的类时,选择此特化。

一个例子:如果你实例化

is_specialization<std::vector<int>, std::map>

选择主版本(继承自 std::false_type)是因为 std::vector&lt;int&gt; 不是基于 std::map

但是如果你实例化

is_specialization<std::vector<int>, std::vector>

之所以选择特化(继承自std::true_type)是因为std::vector&lt;int&gt; 基于std::vector

【讨论】:

  • 我需要把这篇文章通读几遍,也许需要几天时间和几杯咖啡哈哈哈
  • @tjwrona1992 - 是的,我想你需要了解模板模板参数的概念;不完全是微不足道的,恕我直言。
  • 我想我已经开始掌握模板-模板参数了,但可变参数模板被混入其中的事实确实让我头晕目眩
  • @tjwrona1992 - 还要考虑到匹配模板模板参数中的模板参数和匹配参数中的模板参数的规则很复杂(并且考虑到我不明白我刚才所说的)写)并随着 C++17 的变化而改变。
  • 这是否意味着在 C++17 中可能有一种更简洁的方式来编写它并达到相同的结果?
【解决方案2】:

这是一个基于导致我脑海中“点击”的原因的答案,如果您可以遵循它,我相信 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,而在第二个参数中它是@987654328 @。 他还用double 替换了Args...,但这并不是那么重要,因为这不是不匹配的原因。

同样,编译器可能首先尝试匹配第二个参数并得出Template 必须是std::set 的结论,然后由于第一个参数不是std::set&lt;Args...&gt; 而失败。

我认为模板的第一行不太重要,也更容易理解,但为了完整起见,让我们回顾一下。

template <class T, template <class...> class Template>

只是表示这是一个模板,其第二个参数是模板模板参数,这是必需的,因此您可以传递类似std::set 作为第二个参数(请注意,通常这不起作用,您需要传递 std::set某种类型,例如std::set&lt;float&gt;)。

最丑的部分是第二个模板的第一行:

template <template <class...> class Template, class... Args>

如果我们还记得我们在专业化代码中所做的事情,那么这里又是有意义的。 我们有模板模板参数Template,并且需要Args 只是因为我们想将模板模板参数用作普通模板参数(作为第一个参数(Template&lt;Args...&gt;))。

tl;dr

忽略模板的第一行,模式匹配。

免责声明:就像我说的那样,这只是我向自己解释这段代码的方式,我知道标准的人会因为阅读“第一行”或“模式匹配”之类的短语而哭泣。

【讨论】:

    猜你喜欢
    • 2013-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-15
    • 2018-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多