【问题标题】:Sum of array elements as constexpr数组元素的总和为 constexpr
【发布时间】:2017-07-02 07:58:07
【问题描述】:

我正在尝试将 const int 数组的总和作为 constexpr,以便我可以使用 sum 作为另一个数组的大小

constexpr int arr[] = {1, 2, 3};
constexpr int sum1 = std::accumulate(arr, arr + 3, 0); // not OK
int arr1[sum1];

由于std::accumulate() 不返回constexpr,以上内容无法编译。我最终有一个像这样的解决方法

template <size_t N>
constexpr int sum(int const a[])
{
    return a[N-1] + sum<N - 1>(a);
}

template <>
constexpr int sum<0>(int const a[])
{
    return 0;
}

constexpr int arr[] = {1, 2, 3};
constexpr int sum1 = sum<3>(arr);
int arr1[sum1];

有没有更简单的解决方案?

【问题讨论】:

  • 数组是1M个元素的时候会变成1M行代码吗?或者它只是成为一个自我重复的功能?
  • 在某些时候,使用代码生成器可能比走这条奇怪的黑客之路更容易。
  • 您可以在类模板上下文中实现这一点 (example)。

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


【解决方案1】:

由于 C++14 的宽松 constexpr,您可以执行以下操作:

#include <iostream>

constexpr int arr[] = {1, 2, 3};

template <size_t Size>
constexpr int sum(const int (&arr)[Size])
{
    int ret = 0;
    for (int i = 0; i < Size; ++i)
        ret += arr[i];
    return ret;
}

int main()
{
    int arr1[sum(arr)];
    std::cout << sizeof(arr1) / sizeof(int);
}

【讨论】:

  • 大小甚至可以推断出来。
【解决方案2】:

使用 C++14:

template<typename T, std::size_t N>
constexpr T array_sum(T (&array)[N]) {
    T sum = 0;
    for (std::size_t i = 0; i < N; i++) {
        sum += array[i];
    }
    return sum;
};

并像这样使用它:

int arr[] = {1, 2, 3};
int brr[array_sum(arr)];

【讨论】:

    【解决方案3】:
    //With C++14
    #include <iostream>
    using namespace std;
    
    template <typename T, std::size_t N>
    constexpr T arraysum(const T (&array)[N])  {
        T sum = 0;
        for (size_t i = 0; i < N; ++i) {
            sum += array[i];
        }
        return sum;
    }
    
    template <typename T, std::size_t N>
    constexpr T arraysize(T (&)[N])  {
        return N;
    }
    
    int main() {
        // your code goes here
        constexpr int arr[] = {1, 2, 3};
        constexpr int size = arraysize(arr);
        cout<<size<<endl;
        constexpr int sum1 = arraysum(arr);
        cout <<sum1;
        int arr2[sum1];
        return 0;
    }
    

    校验码here

    【讨论】:

      【解决方案4】:

      +1 用于 C++14 解决方案,但我提出了一个基于简单递归 constexpr 函数的 C++11 解决方案

      template <typename T, std::size_t N>
      constexpr T aSum (T const (&a)[N], std::size_t i = 0U)
       { return i < N ? (a[i] + aSum(a, i+1U)) : T{}; }
      

      所以可以写

      constexpr int arr[] {1, 2, 3};
      constexpr int sum1 { aSum(arr) };
      

      sum1 变成6

      【讨论】:

        【解决方案5】:

        有一些复杂的代码,但可以使用 C++11,并且只需要 O(log(N)) 深度递归。

        template<typename Iterator >
        constexpr size_t c_dist(Iterator first, Iterator last){ return (last - first); }
        
        template< typename Iterator, typename U>
        constexpr U c_accumulate(Iterator first, Iterator last, U u)
        {
            return (first == last) 
                    ? u 
                    : c_dist(first , last) == 1 
                         ? ( u + *first ) 
                         :  
                            c_accumulate(first, first + c_dist(first, last ) / 2, u ) +  
                            c_accumulate(first + c_dist(first, last)/2, last, u);
        
        }
        
        constexpr int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21};
        constexpr int sum = c_accumulate(a, a + sizeof(a)/sizeof(a[0]), 0);
        
        static_assert(sum == 21*22/2, "!");
        

        【讨论】:

        • 递归的深度无关紧要,因为它将在编译时计算。此外,即使在运行时调用,编译器也很可能会取消递归。
        • 我的意思是编译时的递归。比较这些:rextester.com/MWJ92273rextester.com/VWDEWT41943
        • 更不用说 -ftemplate-depth=n 默认为 n=900,这意味着编译器只允许递归 900 级深度。如果您想对大于 900 的 constexpr 数组求和并且不关心荒谬的编译时间,如果您不使用 @KhurshidNormuradov 之类的 O(log(n)) 方法,则必须手动设置此标志
        • -ftemplate-depth=n 对大项目没有帮助,当使用很多模板算法时,比如 boost.MSM(有限状态机),boost.MPL,我每次都喜欢 O(log(n )) 编译时算法比手动设置为标志。
        猜你喜欢
        • 1970-01-01
        • 2015-09-20
        • 2014-04-06
        • 2018-01-28
        • 1970-01-01
        • 2017-12-06
        • 1970-01-01
        • 2019-04-21
        • 2020-12-08
        相关资源
        最近更新 更多