[英]Template in C++, why have to use enum
我有一个关于 Scott Meyers 的“Effective C++”中第 48 项的快速问题。 我只是不明白从下面的书中复制的代码,
#include <iostream>
using namespace std;
template <unsigned n>
struct Factorial
{
enum { value=n*Factorial<n-1>::value };
};
template <>
struct Factorial<0>
{
enum { value=1};
};
int main()
{
cout<<Factorial<5>::value<<endl;
cout<<Factorial<10>::value<<endl;
}
为什么我必须在模板编程中使用枚举? 有没有其他方法可以做到这一点? 我在这里先向您的帮助表示感谢。
您也可以使用static const int
:
template <unsigned n>
struct Factorial
{
static const int value= n * Factorial<n-1>::value;
};
template <>
struct Factorial<0>
{
static const int value= 1;
};
这也应该没问题。 两种情况下的结果相同。
或者您可以使用现有的类模板,例如std::integral_constant
(仅在 C++11 中)作为:
template <unsigned n>
struct Factorial : std::integral_constant<int,n * Factorial<n-1>::value> {};
template <>
struct Factorial<0> : std::integral_constant<int,1> {};
我看到其他答案很好地涵盖了替代方法,但没有人解释为什么需要enum
(或static const int
)。
首先,考虑以下非模板等效项:
#include <iostream>
int Factorial(int n)
{
if (n == 0)
return 1;
else
return n * Factorial(n-1);
}
int main()
{
std::cout << Factorial(5) << std::endl;
std::cout << Factorial(10) << std::endl;
}
你应该能够很容易地理解它。 然而,它的缺点是阶乘的值将在运行时计算,即在运行您的程序后,编译器将执行递归函数调用和计算。
模板方法的想法是在编译时执行相同的计算,并将结果放入生成的可执行文件中。 换句话说,您提供的示例解析为类似的内容:
int main()
{
std::cout << 120 << std::endl;
std::cout << 3628800 << std::endl;
}
但是为了实现这一点,您必须“欺骗”编译器来执行计算。 为了做到这一点,您需要让它将结果存储在某个地方。
enum
正是为了做到这一点。 我将尝试通过指出在那里不起作用的方式来解释这一点。
如果您尝试使用常规int
,它将不起作用,因为像int
这样的非静态成员仅在实例化对象中才有意义。 你不能像这样给它赋值,而是在构造函数中这样做。 一个普通的int
行不通的。
你需要一些可以在未实例化的类上访问的东西。 您可以尝试static int
但它仍然不起作用。 clang
会给你一个非常简单的问题描述:
c.cxx:6:14: error: non-const static data member must be initialized out of line
static int value=n*Factorial<n-1>::value ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
如果您实际上将这些定义放在外,代码将编译,但它会导致两个0
s。 这是因为这种形式将值的计算延迟到程序的初始化,并且不能保证正确的顺序。 很可能在计算之前获得了Factorial<n-1>::value
s,因此返回0
。 此外,它仍然不是我们真正想要的。
最后,如果你把static const int
放在那里,它会按预期工作。 那是因为必须在编译时计算static const
,而这正是我们想要的。 让我们再次输入代码:
#include <iostream>
template <unsigned n>
struct Factorial
{
static const int value=n*Factorial<n-1>::value ;
};
template <>
struct Factorial<0>
{
static const int value=1;
};
int main()
{
std::cout << Factorial<5>::value << std::endl;
std::cout << Factorial<10>::value << std::endl;
}
首先你实例化Factorial<5>
; static const int
强制编译器必须在编译器时计算其值。 实际上,它在必须计算另一个值时实例化Factorial<4>
类型。 这一直持续到它到达Factorial<0>
,在那里可以计算值而无需进一步实例化。
所以,这是另一种方式和解释。 我希望它至少对理解代码有一点帮助。
您可以将这种模板视为我在开头发布的递归函数的替代品。 你只需替换:
return x;
使用static const int value = ...
,f(x-1)
与t<x-1>::value
,if (n == 0)
与专业化struct Factorial<0>
。 对于enum
本身,正如已经指出的那样,它在示例中用于强制执行与static const int
相同的行为。 就像那样,因为所有的enum
值都需要在编译时知道,所以每个请求的值都必须在编译时计算。
更具体地说,“enum hack”的存在是因为当时的许多编译器不支持使用static const int
更正确的方法。 它在现代编译器中是多余的。
你可以像 Nawaz 所说的那样使用static const int
。 我猜 Scott Myers 使用枚举的原因是,当他写这本书时,编译器对静态常量整数的类内初始化的支持有点有限。 所以枚举是一个更安全的选择。
您可以使用 int 而不是 static const 它,如下所示:
template<int n>
struct Factorial
{
int val{ n*Factorial<n - 1>().val };
};
template<>
struct Factorial<0>
{
int val{1};
};
int main()
{
cout << "Factorial 5: " << Factorial<5>().val << endl;
}
您还可以使用函数模板而不是结构/类模板:
template<int n>
int fact()
{
return n*fact<n - 1>();
}
template <>
int fact<0>()
{
return 1;
}
int main()
{
cout << "Fact 5: " << fact<5>() << endl;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.