如何使用元编程C ++展开循环?

时间:2011-12-09 17:56:43

标签: c++ metaprogramming

事实上,我想评估2个数组的点积。当我尝试这个时

template <int N, typename ValueType>
struct ScalarProduct {
    static ValueType product (ValueType* first, ValueType* second) {
        return ScalarProduct<N-1, ValueType>::product(first + 1, second + 1) 
            + *first * *second;
    }
};

template <typename ValueType>
struct ScalarProduct<0, ValueType> {
    static ValueType product (ValueType* first, ValueType* second) {
        return 0;
    }

然后在运行时计算的时间少于编译期间

3 个答案:

答案 0 :(得分:3)

首先,您正在编写函数。那么元编程与否,编译器将生成函数。并且由于函数不会在运行时进行评估,因此您的方法不会减少运行时间。实际上,当您将for循环展开到递归函数调用中时,它可能会增加一些开销。

要回答更通用的问题,使用模板元编程,您只能在编译时计算内容。标准方法是预先计算所需的值并将它们存储为对象中的成员。并且您只能使用枚举类型(不需要构造函数的类型)在编译时计算内容,因为所有构造函数调用都是在运行时执行的。

在大多数情况下,元编程并不实用。您可以将它用作学习模板的好工具,但它会导致大型二进制文件和不可维护的代码库。所以我建议你不要使用它,除非你已经探索了查找表等其他选项。

如果已经在代码中定义了任意数组,则只能使用它们。例如

int a1[] = {1,2,3};
int a2[] = {2,4,5};

template <int N,typename T>
struct foo {
  int product;
  foo<N-1,T> rest;
  foo(const T* array1,const T* array2) : rest(array1+1,array2+1) { product = array1[0] * array2[0] + rest.product; }
};

template <0,typename T>
struct foo {
  int product;
  // These addresses are stale, so don't use them
  foo(cons T* array1, const T* array2) : product(0) {}
};

foo<3,int> myfoo(a1,a2);

你可以使用myfoo.product来获取编译时计算的a1和a2的叉积的值。

答案 1 :(得分:0)

编译器可能会为您执行此操作,但可能不会。您必须查看编译器生成的代码,以确定它是否成功。如果由于某种原因它没有,你唯一的选择是不使用递归模板并提出替代解决方案,即查找表,运行时循环等。

答案 2 :(得分:0)

如果您想了解为什么两段代码的表现不同,则需要一个分析器。

如果我不得不猜测,我会说你聪明的递归模板扩展会产生很难让编译器高效优化的代码。浮点数组上的循环可能是C ++中最积极优化的构造;你的编译器甚至可能明确地为标量产品提供特殊情况。当然,它能够展开自己的循环,如果它想要的话。

在这些简单的事情中,最好不要试图欺骗编译器执行你想要的优化。它比您更了解标量产品,并且能够更好地有效地解决您的问题。真!

或许我完全错了,还有其他一些事情应该归咎于。也许你的基准是错误的,也许你的样本量太小了。也许任何事情。你不应该试着猜出为什么会发生这种奇怪的结果;你应该应用一个分析器,并进行调查。

祝你好运。