2016-09-09 44 views
4

假設我們有這樣的代碼。它運作良好,並預先計算出前5個斐波納契數字。使用模板元編程計算數據編譯時間

#include <iostream> 

template <int T> 
struct fib; 

template <> 
struct fib<0>{ 
    constexpr static int value = 1; 
}; 

template <> 
struct fib<1>{ 
    constexpr static int value = 1; 
}; 

template <int I> 
struct fib{ 
    constexpr static int value = fib<I - 1>::value + fib<I - 2>::value; 
}; 

int main(){ 
    std::cout << fib<0>::value << std::endl; 
    std::cout << fib<1>::value << std::endl; 
    std::cout << fib<2>::value << std::endl; 
    std::cout << fib<3>::value << std::endl; 
    std::cout << fib<4>::value << std::endl; 
    std::cout << fib<5>::value << std::endl; 
} 

然而,它有一個「小」的問題。

如果我們需要將這個值用於編譯時未知的值,該怎麼辦?

對於一些值,我們可以這樣做:

const int max = 5; 

int getData(){ 
    return 5; // return value between 0 and max. 
} 

int something(){ 
    switch(getData()){ 
     case 0: return fib<0>::value; 
     case 1: return fib<1>::value; 
     case 2: return fib<2>::value; 
     case 3: return fib<3>::value; 
     case 4: return fib<4>::value; 
     case 5: return fib<5>::value; 
    } 
} 

這將工程確定爲5個值,但是如果我們有150或300?
是不是真的認真地改變300行的代碼...

這裏有什麼可以解決的辦法嗎?

+1

你可以創建一個靜態數組並在運行時按照e查找。 G。 http://stackoverflow.com/questions/37297359/sequence-array-initialization-with-template – xcvii

回答

4

如果您需要在運行時使用在編譯時未知的值,則無法在編譯時計算它。明顯。

但是...如果您可以對所需的值施加最高值,則可以在編譯時計算所有值(從零到頂)並將它們存儲在std::array中。

在下面的例子中,我已修改了fib結構(使用該值的std::size_t索引和模板類型(具有默認unsigned long)),並且我添加了一個模板struct fibVals其包含使用fib<n>::value

初始化的 std::array

以下main()表明可以定義constexpr fibvals<N>(在該示例中爲N == 20)以計算(在編譯時)在範圍[0,N [中的所有fib<n>值。

#include <array> 
#include <utility> 
#include <iostream> 

template <std::size_t, typename T = unsigned long> 
struct fib; 

template <typename T> 
struct fib<0U, T> 
{ constexpr static T value { T(1) }; }; 

template <typename T> 
struct fib<1U, T> 
{ constexpr static T value { T(1) }; }; 

template <std::size_t I, typename T> 
struct fib 
{ constexpr static T value { fib<I-1U>::value + fib<I-2U>::value }; }; 

template <std::size_t I, typename T = unsigned long> 
struct fibVals 
{ 
    const std::array<T, I> vals; 

    template <std::size_t ... Is> 
    constexpr fibVals (std::index_sequence<Is...> const &) 
     : vals { { fib<Is, T>::value ... } } 
    { } 

    constexpr fibVals() : fibVals { std::make_index_sequence<I> { } } 
    { } 
}; 


int main() 
{ 
    constexpr fibVals<20> fv; 

    for (auto ui = 0U ; ui < fv.vals.size() ; ++ui) 
     std::cout << "fib(" << ui << ") = " << fv.vals[ui] << std::endl; 
} 

不幸的是該實施例中使用std::make_index_sequence<I>std::index_sequence<Is...>是C++ 14層的功能。

如果你想實現C++ 11 struct fibVals,可以實現以下結構struct indexSeqstruct indexSeqHelper,替代std::index_sequence<Is...>std::make_index_sequence<I>

template <std::size_t ...> 
struct indexSeq 
{ }; 

template <std::size_t N, std::size_t ... Next> 
struct indexSeqHelper 
{ using type = typename indexSeqHelper<N-1U, N-1U, Next ... >::type; }; 

template <std::size_t ... Next > 
struct indexSeqHelper<0U, Next ... > 
{ using type = indexSeq<Next ... >; }; 

和實施fibVals構造如下

template <std::size_t ... Is> 
constexpr fibVals (indexSeq<Is...> const &) 
    : vals { { fib<Is, T>::value ... } } 
{ } 

constexpr fibVals() : fibVals { typename indexSeqHelper<I>::type { } } 
{ } 
+0

你能解釋一下到底發生了什麼: vals {{fib :: value ...}}。我嘗試了不同的功能,它仍然可以正常工作,但省略號的確切程度是...在這裏產生一個聚合序列? – Nick

+0

是不是在運行時「填充」數組? – Nick

+2

@Nick - 省略號用'Is'擴展前面的組件;在我們的情況下'fib :: value'。所以'vals {{fib :: value ...}}''vals {{fib <0, T> :: value,fib <1, T> :: value,fib <2, T> :: value,fib <3, T> :: value,fib <4, T> :: value ,[...] fib <18, T> :: value,fib <19, T> :: value}}' – max66

1

模板在編譯時進行評估,因此模板在運行時沒有解決方案。

您可以製作constexpr函數,其中可能根據傳遞的值在編譯時進行評估。顯然,編譯時可能不會計算運行時值,因爲它在編譯時並不知道。