简体   繁体   English

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

[英]Template in C++, why have to use enum

I have a quick question about item 48 in Scott Meyers' "Effective C++".我有一个关于 Scott Meyers 的“Effective C++”中第 48 项的快速问题。 I just don't understand the code copied from the book below,我只是不明白从下面的书中复制的代码,

    #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;
    }

Why do I have to use enum in template programming?为什么我必须在模板编程中使用枚举? Is there an alternative way to do this?有没有其他方法可以做到这一点? Thanks for the help in advance.我在这里先向您的帮助表示感谢。

You could use static const int also:您也可以使用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;
};

This should be fine also.这也应该没问题。 The result is same in both cases.两种情况下的结果相同。

Or you could use existing class template, such as std::integral_constant (in C++11 only) as:或者您可以使用现有的类模板,例如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> {};

I see that the other answers cover the alternative approaches well, but no one's explained why the enum (or static const int ) is required.我看到其他答案很好地涵盖了替代方法,但没有人解释为什么需要enum (或static const int )。

First, consider the following non-template equivalent:首先,考虑以下非模板等效项:

#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;
}

You should be able to understand it easily.你应该能够很容易地理解它。 However, it's disadvantage is that the value of the factorial will be computed at run-time, ie after running your program the compiler will execute the recursive function calls and calculations.然而,它的缺点是阶乘的值将在运行时计算,即在运行您的程序后,编译器将执行递归函数调用和计算。

The idea of template approach is to perform the same calculations at compile-time, and place the result in the resulting executable.模板方法的想法是在编译时执行相同的计算,并将结果放入生成的可执行文件中。 In other words, the example you presented resolves to something alike:换句话说,您提供的示例解析为类似的内容:

int main()
{
    std::cout << 120 << std::endl;
    std::cout << 3628800 << std::endl;
}

But in order to achieve that, you have to 'trick' the compiler into performing the computations.但是为了实现这一点,您必须“欺骗”编译器来执行计算。 And in order to do that, you need to let it store the result somewhere.为了做到这一点,您需要让它将结果存储在某个地方。

The enum is there exactly in order to do that. enum正是为了做到这一点。 I will try to explain that by pointing out what would not work there.我将尝试通过指出在那里不起作用的方式来解释这一点。

If you tried to use a regular int , it would not work because a non-static member like int is meaningful only in a instantiated object.如果您尝试使用常规int ,它将不起作用,因为像int这样的非静态成员仅在实例化对象中才有意义。 And you can't assign a value to it like this but instead do that in a constructor.你不能像这样给它赋值,而是在构造函数中这样做。 A plain int won't work.一个普通的int行不通的。

You need something that would be accessible on an uninstantiated class instead.你需要一些可以在未实例化的类上访问的东西。 You could try static int but it still doesn't work.您可以尝试static int但它仍然不起作用。 clang would give you a pretty straightforward description of the problem: 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 ;
                           ^     ~~~~~~~~~~~~~~~~~~~~~~~

If you actually put those definitions out-of-line, the code will compile but it will result in two 0 s.如果您实际上将这些定义放在外,代码将编译,但它会导致两个0 s。 That is because this form delays the computation of values to the initialization of program, and it does not guarantee the correct order.这是因为这种形式将值的计算延迟到程序的初始化,并且不能保证正确的顺序。 It is likely that a Factorial<n-1>::value s was obtained before being computed, and thus 0 was returned.很可能在计算之前获得了Factorial<n-1>::value s,因此返回0 Additionally, it is still not what we actually want.此外,它仍然不是我们真正想要的。

Finally, if you put static const int there, it will work as expected.最后,如果你把static const int放在那里,它会按预期工作。 That's because static const has to be computed at the compile time, and that's exactly what we want.那是因为必须在编译时计算static const ,而这正是我们想要的。 Let's type the code again:让我们再次输入代码:

#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;
}

First you instantiate Factorial<5> ;首先你实例化Factorial<5> ; static const int forces the compiler has to compute its value at compiler time. static const int强制编译器必须在编译器时计算其值。 Effectively, it instantiates the type Factorial<4> when it has to compute another value.实际上,它在必须计算另一个值时实例化Factorial<4>类型。 And this goes one until it hit Factorial<0> where the value can be computed without further instantiations.这一直持续到它到达Factorial<0> ,在那里可以计算值而无需进一步实例化。

So, that was the alternate way and the explanation.所以,这是另一种方式和解释。 I hope it was at least a bit helpful in understanding the code.我希望它至少对理解代码有一点帮助。

You can think of that kind of templates as a replacement of the recursive function I posted at the beginning.您可以将这种模板视为我在开头发布的递归函数的替代品。 You just replace:你只需替换:

  • return x; with static const int value = ... ,使用static const int value = ...
  • f(x-1) with t<x-1>::value , f(x-1)t<x-1>::value
  • and if (n == 0) with the specialization struct Factorial<0> .if (n == 0)与专业化struct Factorial<0>

And for the enum itself, as it was already pointed out, it was used in the example to enforce the same behavior as static const int .对于enum本身,正如已经指出的那样,它在示例中用于强制执行与static const int相同的行为。 It is like that because all the enum values need to be known at compile-time, so effectively every requested value has to be computed at compile-time.就像那样,因为所有的enum值都需要在编译时知道,所以每个请求的值都必须在编译时计算。

To be more specific, the "enum hack" exists because the more correct way of doing it with static const int was not supported by many compilers of the time.更具体地说,“enum hack”的存在是因为当时的许多编译器不支持使用static const int更正确的方法。 It's redundant in modern compilers.它在现代编译器中是多余的。

You can use static const int as Nawaz says.你可以像 Nawaz 所说的那样使用static const int I guess the reason Scott Myers uses an enum is that compiler support for in-class initialization of static const integers was a bit limited when he wrote the book.我猜 Scott Myers 使用枚举的原因是,当他写这本书时,编译器对静态常量整数的类内初始化的支持有点有限。 So an enum was a safer option.所以枚举是一个更安全的选择。

You can use an int instead of static const it for this as follows:您可以使用 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;
}

You can also use function template instead of struct/class template:您还可以使用函数模板而不是结构/类模板:

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