用C++模板,为什么要使用枚举

Min*_*ing 8 c++ effective-c++

关于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)

为什么我必须在模板编程中使用枚举?有没有其他方法可以做到这一点?我在这里先向您的帮助表示感谢.

Naw*_*waz 8

你也可以使用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)


Mic*_*rny 5

我看到其他答案很好地涵盖了替代方法,但没有人解释为什么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值都需要在编译时知道,因此有效地每个请求的值都必须在编译时计算.