编译时相当于 std::accumulate()
Compile-time equivalent to std::accumulate()
我试图编写一个基本的编译时版本的std::accumulate()
,方法是定义一个class模板,该模板将递归地遍历给定的范围,并且将在每次迭代时添加元素。
在 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;
代码如下:
#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;
问: 编写 class 模板(或任何其他 "compile-time" 计算机制)以获得与 std::accumulate()
?
(理想情况下,应该能够像真正的 std::accumulate()
一样传递自定义范围和二元运算)
编辑: 代码中使用的 std::vector
只是一个示例。我也尝试过 std::array
和 C 风格的数组,但我仍然遇到类似的问题。
EDIT2:我不想使用宏。
EDIT3: 我不想使用外部库。这里的目标是做一个简单的、独立的 编译时计算 块。 Class 模板是我的第一个想法,但我对其他想法持开放态度 suggestions/techniques。
std::vector
,与任何其他内存管理容器一样,当然在编译期间不可迭代。鉴于标签 C++11,我建议使用 boost.fusion
:)
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);
}
对于原始数组,如果还声明了它们 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);
}
现在在 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;
}
或
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;
}
我试图编写一个基本的编译时版本的std::accumulate()
,方法是定义一个class模板,该模板将递归地遍历给定的范围,并且将在每次迭代时添加元素。
在 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;
代码如下:
#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;
问: 编写 class 模板(或任何其他 "compile-time" 计算机制)以获得与 std::accumulate()
?
(理想情况下,应该能够像真正的 std::accumulate()
一样传递自定义范围和二元运算)
编辑: 代码中使用的 std::vector
只是一个示例。我也尝试过 std::array
和 C 风格的数组,但我仍然遇到类似的问题。
EDIT2:我不想使用宏。
EDIT3: 我不想使用外部库。这里的目标是做一个简单的、独立的 编译时计算 块。 Class 模板是我的第一个想法,但我对其他想法持开放态度 suggestions/techniques。
std::vector
,与任何其他内存管理容器一样,当然在编译期间不可迭代。鉴于标签 C++11,我建议使用 boost.fusion
:)
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);
}
对于原始数组,如果还声明了它们 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);
}
现在在 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;
}
或
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;
}