关于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;
}
Run Code Online (Sandbox Code Playgroud)
为什么我必须在模板编程中使用枚举?有没有其他方法可以做到这一点?我在这里先向您的帮助表示感谢.
你也可以使用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;
};
Run Code Online (Sandbox Code Playgroud)
这也应该没问题.结果在两种情况下都是相同的.
或者您可以使用现有的类模板,例如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> {};
Run Code Online (Sandbox Code Playgroud)
我看到其他答案很好地涵盖了替代方法,但没有人解释为什么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;
}
Run Code Online (Sandbox Code Playgroud)
你应该能够轻松地理解它.但是,它的缺点是factorial的值将在运行时计算,即在运行程序之后,编译器将执行递归函数调用和计算.
模板方法的想法是在编译时执行相同的计算,并将结果放在生成的可执行文件中.换句话说,你提出的例子解决了类似的事情:
int main()
{
std::cout << 120 << std::endl;
std::cout << 3628800 << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
但是为了实现这一点,你必须"欺骗"编译器来执行计算.为了做到这一点,你需要让它将结果存储在某个地方.
的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 ;
^ ~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
如果你实际上把这些定义放在了外面,那么代码将会编译,但是它会产生两个0s.这是因为这种形式将值的计算延迟到程序的初始化,并且它不保证正确的顺序.很可能Factorial<n-1>::value在计算之前获得了s,因此0返回了s .此外,它仍然不是我们真正想要的.
最后,如果你放在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;
}
Run Code Online (Sandbox Code Playgroud)
首先你实例化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值都需要在编译时知道,因此有效地每个请求的值都必须在编译时计算.