計算因子

可以使用模板超程式設計技術在編譯時計算因子。

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