簡體   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