【问题标题】:Compile-time equivalent to std::accumulate()编译时等价于 std::accumulate()
【发布时间】:2015-10-15 20:16:32
【问题描述】:

我尝试编写一个基本的编译时版本的std::accumulate(),方法是定义一个类模板,该模板将递归迭代给定范围并在每次迭代时添加元素。

Ubuntu 14.04 上使用gcc 4.8.4 编译测试程序时,出现以下错误:

compile-time-accumulate.cpp: In function ‘int main()’:
compile-time-accumulate.cpp:44:40: error: call to non-constexpr function ‘std::vector<_Tp, _Alloc>::const_iterator std::vector<_Tp, _Alloc>::cbegin() const [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::const_iterator = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >; typename __gnu_cxx::__alloc_traits<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type>::const_pointer = const int*]’
                               v.cbegin(),
                                        ^
compile-time-accumulate.cpp:46:32: error: ‘class __gnu_cxx::__normal_iterator<const int*, std::vector<int> >’ is not a valid type for a template non-type parameter
                               0>::value;

代码如下:

Run it online

#include <iostream>
#include <vector>

template
<
    typename     ResultType,
    typename     IteratorType,
    IteratorType Iterator,
    int          RangeLength,
    ResultType   InitialValue
>
struct accumulator
{
    static_assert(RangeLength > 1, "The range length must be > 1");
    static constexpr ResultType value = InitialValue
                                      + accumulator<ResultType,
                                                    IteratorType,
                                                    Iterator + 1,
                                                    RangeLength - 1,
                                                    *Iterator>::value;
};

template
<
    typename     ResultType,
    typename     IteratorType,
    IteratorType Iterator,
    //int          RangeLength,
    ResultType   InitialValue
>
struct accumulator<ResultType, IteratorType, Iterator, 1, InitialValue>
{
    static constexpr ResultType value = InitialValue + *Iterator;
};


int main()
{
    std::vector<int> v = {4,5,6,7};

    const int a = accumulator<int,
                              decltype(v.cbegin()),
                              v.cbegin(),
                              4,
                              0>::value;

    std::cout << a << std::endl;
}

所以基本上,标准不允许在模板参数中使用变量,这就是我在这里所做的:

const int a = accumulator<int,
                          decltype(v.cbegin()),
                          v.cbegin(),
                          4,
                          0>::value;

问:编写类模板(或任何其他“编译时”计算机制)以实现与std::accumulate() 类似的结果的正确方法是什么?

(理想情况下,应该能够像真正的std::accumulate() 一样传递自定义范围和二进制操作)

编辑: 代码中使用的std::vector 只是一个示例。我也尝试过 std::array 和 C 样式的数组,但我仍然遇到类似的问题。

EDIT2:我不想使用宏。

EDIT3:我不想使用外部库。这里的目标是做一个简单的、自包含的编译时计算块。类模板是我的第一个想法,但我愿意接受其他建议/技术。

【问题讨论】:

  • std::vector 存储在运行时分配。因此,在编译期间不可能遍历std::vector
  • 如果您使用 integer_sequence 而不是向量,我认为您可以更轻松地完成此操作。
  • 你可以遍历std::array,因为它的大小在编译时是已知的。 std::vector 你不能这样做
  • 有一组 constexpr 容器here;还提供了accumulate

标签: c++ templates c++11 template-meta-programming gcc4.8


【解决方案1】:

std::vector 的存储空间是在运行时分配的。因此,在编译期间不可能迭代 std::vector。

现在用于std::array 和原始数组。假设您的 std::array 变量是 constexpr 您可以使用以下构造来累积它:

template<typename T, std::size_t N>
constexpr T compile_time_accumulator(std::array<T, N> const &A, int const i = 0) {
  return (i < N)? A[i] + compile_time_accumulator(A, i + 1) : T(0);
}

LIVE DEMO

对于原始数组,如果它们被声明为constexpr,则如下构造:

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

LIVE DEMO

现在在 C++14 中,关于 constexpr 的事情变得更加轻松,您可以执行以下操作:

template<typename T, std::size_t N>
constexpr T compile_time_accumulator(T const (&A)[N]) {
  T sum(T(0));

  for(int i = 0; i < N; ++i) {
    sum += A[i];
  }

  return sum;
}

LIVE DEMO

template<typename T, std::size_t N>
constexpr T compile_time_accumulator(std::array<T, N> const &A) {
  T sum(T(0));

  for(int i = 0; i < N; ++i) {
    sum += A[i];
  }

  return sum;
}

LIVE DEMO

【讨论】:

  • 有趣,我extended your example 是为了采用自定义 lambda,这似乎运作良好。谢谢!
  • @865719 不错,但我建议将std::plus 用作compile_time_accumulator&lt;int, 4&gt;(v, 0, std::plus&lt;int&gt;(), 42)
  • 我同意 :) 在相关说明中:在阅读了更多关于 constexpr 的信息(至少在 C++11 中)之后,似乎从 constexpr 函数调用 std::accumulate()是合法的(参见this example。但是,我们是否保证constexpr 函数的代码(即代码示例中的ct_std_accumulate_std())将在编译时执行?
  • @865719 很抱歉让您失望了,但std::accumulate 不是constexpr。因此,您的示例不是编译时评估,而是运行时评估。
  • @865719 还不允许使用 constexpr lambda。必须是接受它们的 gcc 错误。 Clang 抱怨他们。我也不知道我已经提出了一个问题here
【解决方案2】:

std::vector 与任何其他内存管理容器一样,当然在编译期间不可迭代。鉴于标签 C++11,我建议使用 boost.fusion :)

【讨论】:

    猜你喜欢
    • 2023-03-15
    • 2010-09-25
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    • 2022-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多