【问题标题】:calculating data compile time with template metaprogramming使用模板元编程计算数据编译时间
【发布时间】:2016-09-09 14:57:02
【问题描述】:

假设我们有这样的代码。它运行良好,可以预先计算前 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行的代码真的不是很严重...

这里有什么解决方法?

【问题讨论】:

标签: c++ c++11 templates template-meta-programming template-specialization


【解决方案1】:

如果您需要在运行时使用编译时未知的值,则无法在编译时计算它。很明显。

但是...如果您可以对所需的值施加最高值,则可以在编译时计算 所有 值(从零到最高)并将它们存储在 std::array 中。 p>

在以下示例中,我修改了您的 fib 结构(以使用 std::size_t 索引和模板类型(默认为 unsigned long)作为值)并添加了一个模板化的 struct fibVals,其中包含使用fib&lt;n&gt;::value 初始化的std::array

以下main() 表明可以定义一个constexpr fibvals&lt;N&gt;(在示例中使用N == 20)来计算(在编译时)范围[0,N[ 中的所有fib&lt;n&gt; 值。

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

不幸的是,这个示例使用了 C++14 特性的 std::make_index_sequence&lt;I&gt;std::index_sequence&lt;Is...&gt;

如果要在 C++11 中实现 struct fibVals,可以实现以下结构体 struct indexSeqstruct indexSeqHelper,以替换 std::index_sequence&lt;Is...&gt;std::make_index_sequence&lt;I&gt;

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 { } }
 { }

【讨论】:

  • 你能解释一下这里到底发生了什么吗:vals { { fib::value ... } } 。我尝试了不同的功能,它仍然可以正常工作,但是省略号到底是如何产生聚合序列的?
  • 它不是在运行时“填充”数组吗?
  • @Nick - 省略号将前面的组件扩展为Is;在我们的例子中fib&lt;Is, T&gt;::value。所以vals{ { fib&lt;Is, T&gt;::value ... } }变成vals{ { fib&lt;0, T&gt;::value, fib&lt;1, T&gt;::value, fib&lt;2, T&gt;::value, fib&lt;3, T&gt;::value, fib&lt;4, T&gt;::value, [...] fib&lt;18, T&gt;::value, fib&lt;19, T&gt;::value } }
  • @Nick - 它可以根据情况“填充”数组编译时间或运行时间。由于fibVals&lt;20&gt; fv 之前的constexpr 强加了fv 的编译时间构造,我的示例强加了编译时间填充。 (继续)
  • @Nick 有关param pack expansion 的补充信息,Andrei Alexandrescu 的Going Native 2012 Variadic Templates are Funadic 的前 20 分钟。
【解决方案2】:

模板在编译时进行评估,因此没有在运行时工作的模板解决方案。

您可以创建一个constexpr 函数,可能在编译时进行评估,具体取决于传递的值。显然,运行时值可能不会在编译时计算,因为它在编译时是未知的。

【讨论】:

    猜你喜欢
    • 2016-06-05
    • 2012-01-25
    • 1970-01-01
    • 2015-09-15
    • 2012-11-03
    • 2011-03-06
    • 2013-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多