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