我正在尝试使用taylor系列扩展构建一个简单的sine函数,可以在编译时使用C++ 14 constexpr对其进行评估。我的代码正在编译,但是编译器不会生成常量。
sine定义如下:

template <int P, typename T = double> constexpr T sine(T x) {
    T result = x;

    for (int i = 1; i < P; ++i)
        result += power<T>(-1, i) * power<T>(x, 1 + 2 * i) / factorial<T>(1 + 2 * i);

    return result;
}

如果需要,我可以提供powerfactorial的代码。它们是琐碎的,也是constexpr

我从这样的循环中调用sine:
template <int N> void test(double *out) {
    for (int i = 0; i < N; ++i) {
        out[i] = sine<20, double>(i * M_PI / N);
    }
}

我期望编译器可以为sine生成一组结果,并将它们放入out中,而无需实际计算taylor系列。相反,生成的代码将像其他非sine函数一样执行constexpr

我的编译器是Xcode 7.2中的lang,使用-O3进行编译。

最佳答案



为了在编译时评估constexpr函数,必须遵循以下条件:

  • 其所有输入参数必须是常量表达式。
  • 必须在常量表达式中使用其结果。
  • test的for循环中的分配不是常量表达式。因此,无法在编译时评估sine

    您真正想要的是使用sine()静态初始化数组的元素。使用std::array和一些辅助机制可以做到这一点,如下所示:
    #define r 0.01745329251
    
    constexpr double factorial(int n) {
      double res = 1.0;
    
      for(int i(2); i <= n; ++i) res *= i;
    
      return res;
    }
    
    template<typename T>
    constexpr T power(T &&base, int const n) {
    
      if(!n) return 0.0;
    
      T res = base;
    
      for(int i(1); i < n; ++i) res *= base;
    
      return res;
    }
    
    template <typename T, int N = 5>
    constexpr T sine(T &&x) {
      T res = x * r;
    
      for (int i(3), sgn(-1); i <= N; i += 2, sgn = -sgn) {
        res += power(x * r, i) / factorial(i);
      }
    
      return res;
    }
    
    template <class T, std::size_t N, std::size_t... Is>
    constexpr std::array<T, N> sine_array_impl(std::index_sequence<Is...>) {
      return {{sine(T{Is})...}};
    }
    
    template <class T, std::size_t N>
    constexpr std::array<T, N> sine_array() {
      return sine_array_impl<T, N>(std::make_index_sequence<N>{});
    }
    

    Live Demo

    10-06 12:44