繁体   English   中英

C++中的模板,为什么必须使用枚举

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM