【问题标题】:How does int*** in this template metaprogram work?这个模板元程序中的 int*** 是如何工作的?
【发布时间】:2016-12-18 07:45:00
【问题描述】:

模板元编程如何在此处 (static const int value = 1 + StarCounter<\U>::value;) 打印出 3

#include <iostream>

template <typename T>
struct StarCounter
{
    static const int value = 0;
};

template <typename U>
struct StarCounter<U*>
{
    static const int value = 1 + StarCounter<U>::value;
};

int main()
{
    std::cout << StarCounter<int***>::value << std::endl;//How is it printing 3?
    return 0;
}

【问题讨论】:

  • 创建代码示例时,请不要省略包含指令。复制粘贴和编译代码真的很痛苦
  • 这是显式特化如何工作的一个非常基本的示例。您是否阅读过任何有关模板显式特化的内容?
  • 这是一个足够简单的案例,您可以自己推断。 StarCounter&lt;int***&gt;StarCounter&lt;U*&gt; 相同,其中Uint**,然后使用StarCounter&lt;int**&gt; 等等...
  • 你能去掉StarCounter&lt;\U&gt;::value里面多余的\ 吗? (我不能进行单字符编辑)

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


【解决方案1】:

第一个模板创建一个结构体,当你调用StarCounter&lt;U&gt;::value时总是返回0。

第二个模板将第一个模板专门用于使用指针的情况。因此,当您使用StarCounter&lt;U*&gt;::value 调用它时,将使用第二个模板,而不是第一个模板,它将返回StarCounter&lt;U&gt;::value + 1。请注意,它会在每个递归步骤中删除指针。

所以对StarCounter&lt;int***&gt;::value 的调用将花费到:

StarCounter<int***>::value // second template is used due to pointer
1 + StarCounter<int**>::value // second template is used due to pointer
1 + 1 + StarCounter<int*>::value // second template is used due to pointer
1 + 1 + 1 + StarCounter<int>::value // no pointer here, so first template is used
1 + 1 + 1 + 0
3

【讨论】:

  • "当你调用 StarCounter&lt;U&gt;::value" 时总是会返回 0——不是真的。这里没有通话。 StarCounter&lt;U&gt;::value 是结构体中的静态字段,其值为0。写 StarCounter&lt;U&gt;::value 只是读取该字段。
  • @wchargin:好吧,考虑到如果不先计算(实例化模板)就无法读取“那个字段”,应该不难看出“调用”词汇的来源(通常,在 TMP 术语中,您将模板实例化视为调用,将模板字段视为所述调用的结果)
【解决方案2】:
StarCounter<int>::value

等于0,因为它与模板的第一个实例化相匹配,其中value 是显式定义的。

StarCounter<int*>::value = 1 + StarCounter<int>::value

等于1,因为StarCounter&lt;int*&gt;StarCounter&lt;U*&gt; 匹配。是的,StarCounter&lt;T&gt; 也可以视为匹配项,但StarCounter&lt;U*&gt; 更具体,这就是为什么首选这个。

同样,

StarCounter<int**>::value = 1 + StarCounter<int*>::value

等于2

StarCounter<int***>::value = 1 + StarCounter<int**>::value

等于3

【讨论】:

    【解决方案3】:

    我发现在元编程方面考虑运行时等效项会有所帮助。在模板元编程中,我们使用部分特化,就像在运行时编程中一样,我们使用递归。主模板用作基本案例,特化用作递归案例。

    考虑以下确定容器大小的递归版本:

    def size(x):
       if empty(x):
           return 0
       else:
           return 1 + size(tail(x))
    

    这相当于您提供的模板代码。主要模板StarCounter&lt;T&gt; 是基本案例。空壳。它的大小 (value) 为零。特化,StarCounter&lt;U*&gt;,是递归的情况。它的大小 (value) 1 加上尾部递归的大小 (StarCounter&lt;U&gt;)。

    在 C++17 中,我们甚至可以更明确地使元编程版本等同于运行时递归版本(这仅作为示例而不是作为编写此代码的一种方式):

    template <class T>
    struct StarCounter {
        static constexpr int calc_value() {
            if constexpr (!std::is_pointer<T>::value) {
                return 0;
            }
            else {
                return 1 + StarCounter<std::remove_pointer_t<T>>::value;
            }
        }
    
        static constexpr int value = calc_value();
    };
    

    【讨论】:

      【解决方案4】:

      有一个模板StarCounter,在它更一般的形式中,常量value 等于0。因此,当您对大多数类型使用此模板并要求 value 时,您将得到 0。

      这个模板还有一个接受指针的特殊版本。 它的实现也有常量value,它等于1(因为我们有一个指针,这意味着我们至少有一颗星)加上这个指针指向的类型的value

      如果是三颗星,我们有:

      StarCounter&lt;int***&gt;::value = 1 + StarCounter&lt;int**&gt;::value (1) + StarCounter&lt;int*&gt;::value (1) + StarCounter&lt;int&gt;::value (0)

      【讨论】:

      • StarCounter&lt;int**&gt;::value 等于 2,而不是 1
      • alexeykuzmin0: (1) 我的意思是1,它用于指针特化方程。
      猜你喜欢
      • 2012-08-27
      • 1970-01-01
      • 1970-01-01
      • 2011-09-16
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      相关资源
      最近更新 更多