2016-09-09 8 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. г. http://stackoverflow.com/questions/37297359/sequence-array-initialization-with-template – xcvii

ответ

4

Если вам нужно использовать значение во время выполнения, которое неизвестно во время компиляции, вы не можете его вычислить во время компиляции. Очевидный.

... Но если вы можете наложить верхнее значение до значений, необходимых, вы можете вычислить все значения (от нуля вверх) во время компиляции и хранить их в std::array.

В следующем примере я изменил свои fib структур (использовать std::size_t индекс и тип шаблона (с умолчанию unsigned long) для значения), и я добавил шаблонный struct fibVals которые содержат std::array, который инициализируется с помощью fib<n>::value

main() показать, что в примере можно определить constexpr fibvals<N> (с использованием N == 20) для вычисления (во время компиляции) всех значений fib<n> в диапазоне [0, 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 функций.

Если вы хотите реализовать struct fibVals в C++ 11, вы можете реализовать следующие struct indexSeq и Структуры struct 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> :: значение, fib <1, T> :: значение, fib <2, T> :: значение, fib <3, T> :: значение, fib <4, T> :: значение , [...] fib <18, T> :: значение, fib <19, T> :: значение}} ' – max66

1

Шаблоны оцениваются во время компиляции, поэтому нет решения с шаблонами, которые работают во время выполнения.

Вы можете сделать функцию constexpr, которая может быть оценена во время компиляции, в зависимости от пройденного значения, может. Очевидно, что время выполнения не может быть вычислено во время компиляции, поскольку оно неизвестно во время компиляции.