計算因子
可以使用模板超程式設計技術在編譯時計算因子。
#include <iostream>
template<unsigned int n>
struct factorial
{
enum
{
value = n * factorial<n - 1>::value
};
};
template<>
struct factorial<0>
{
enum { value = 1 };
};
int main()
{
std::cout << factorial<7>::value << std::endl; // prints "5040"
}
factorial
是一個結構,但在模板超程式設計中,它被視為模板元函式。按照慣例,模板元函式通過檢查特定成員來評估,::type
用於產生型別的元函式,或::value
用於生成值的元函式。
在上面的程式碼中,我們通過使用我們想要傳遞的引數例項化模板來評估 factorial
元函式,並使用::value
來獲得評估結果。
元函式本身依賴於以較小的值遞迴地例項化相同的元函式。factorial<0>
專業化代表終止條件。模板超程式設計具有函數語言程式設計語言的大多數限制,因此遞迴是主要的迴圈構造。
由於模板元函式在編譯時執行,因此它們的結果可用於需要編譯時值的上下文中。例如:
int my_array[factorial<5>::value];
自動陣列必須具有編譯時定義的大小。元函式的結果是編譯時常量,因此可以在這裡使用。
限制 :大多數編譯器不允許遞迴深度超出限制。例如,g++
編譯器預設情況下將遞迴限制為 256 級。對於 g++
,程式設計師可以使用 -ftemplate-depth-X
選項設定遞迴深度。
Version >= C++ 11
從 C++ 11 開始,std::integral_constant
模板可以用於這種模板計算:
#include <iostream>
#include <type_traits>
template<long long n>
struct factorial :
std::integral_constant<long long, n * factorial<n - 1>::value> {};
template<>
struct factorial<0> :
std::integral_constant<long long, 1> {};
int main()
{
std::cout << factorial<7>::value << std::endl; // prints "5040"
}
此外,constexpr
功能成為更清潔的選擇。
#include <iostream>
constexpr long long factorial(long long n)
{
return (n == 0) ? 1 : n * factorial(n - 1);
}
int main()
{
char test[factorial(3)];
std::cout << factorial(7) << '\n';
}
factorial()
的主體被寫為單個語句,因為在 C++ 11 中,constexpr
函式只能使用非常有限的語言子集。
Version >= C++ 14
從 C++ 14 開始,對 constexpr
函式的許多限制已被刪除,現在可以更方便地編寫它們:
constexpr long long factorial(long long n)
{
if (n == 0)
return 1;
else
return n * factorial(n - 1);
}
甚至:
constexpr long long factorial(int n)
{
long long result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
Version >= C++ 17
從 c ++ 17 開始,可以使用 fold 表示式來計算階乘:
#include <iostream>
#include <utility>
template <class T, T N, class I = std::make_integer_sequence<T, N>>
struct factorial;
template <class T, T N, T... Is>
struct factorial<T,N,std::index_sequence<T, Is...>> {
static constexpr T value = (static_cast<T>(1) * ... * (Is + 1));
};
int main() {
std::cout << factorial<int, 5>::value << std::endl;
}