模板元编程如何在此工作(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;
}
答案 0 :(得分:18)
第一个模板创建一个结构,当您调用StarCounter<U>::value
时,该结构将始终返回0。
对于使用指针的情况,第二个模板专门针对第一个模板。因此,当您使用StarCounter<U*>::value
调用它时,将使用第二个模板,而不是第一个模板,它将返回StarCounter<U>::value + 1
。请注意,它会在每个递归步骤中删除指针。
所以对StarCounter<int***>::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
答案 1 :(得分:11)
StarCounter<int>::value
等于0
,因为它与模板的第一次实例化相匹配,其中value
已明确定义。
StarCounter<int*>::value = 1 + StarCounter<int>::value
等于1
,因为StarCounter<int*>
与StarCounter<U*>
匹配。是的,StarCounter<T>
也可以视为匹配,但StarCounter<U*>
更具体,这就是为什么首选这一点的原因。
类似地,
StarCounter<int**>::value = 1 + StarCounter<int*>::value
等于2
和
StarCounter<int***>::value = 1 + StarCounter<int**>::value
等于3
。
答案 2 :(得分:1)
我发现在元编程方面考虑运行时的等价物会有所帮助。在模板元编程中,我们使用部分特化,如在运行时编程中,我们使用递归。主模板用作基本案例,专门化用作递归案例。
考虑以下递归版本来确定容器的大小:
def size(x):
if empty(x):
return 0
else:
return 1 + size(tail(x))
这相当于您提供的模板代码。主要模板StarCounter<T>
是基本案例。空案。它的大小(value
)为零。专业化StarCounter<U*>
是递归案例。它的大小(value
)1
加上尾部递归的大小(StarCounter<U>
)。
在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();
};
答案 3 :(得分:0)
有一个模板StarCounter
,其中更通用的格式是常量value
等于0
。因此,当您对大多数类型使用此模板并请求value
时,您将得到0。
此模板还有一个接受指针的专用版本。
它的实现也有常量value
,它等于1(因为我们有一个指针,这意味着我们至少有一个星)加上该指针指向的value
类型的值。
如果有三颗星,我们有:
StarCounter<int***>::value = 1 + StarCounter<int**>::value (1) + StarCounter<int*>::value (1) + StarCounter<int>::value (0)