C ++ 11:编译数组的时间计算
假设我有一些constexpr函数f:
constexpr int f(int x) { ... }
我在编译时有一些const int N:
或
#define N ...;
要么
const int N = ...;
根据您的答案需要。
我想要一个int数组X:
int X[N] = { f(0), f(1), f(2), ..., f(N-1) }
这样在编译时计算函数,X中的条目由编译器计算,结果放在我的应用程序映像的静态区域,就好像我在我的X初始化列表中使用了整数文字一样。
有什么方法可以写这个吗? (例如使用模板或macros等)
我有最好的(谢谢柔印)
#include <iostream> #include <array> using namespace std; constexpr int N = 10; constexpr int f(int x) { return x*2; } typedef array<int, N> A; template<int... i> constexpr A fs() { return A{{ f(i)... }}; } template<int...> struct S; template<int... i> struct S<0,i...> { static constexpr A gs() { return fs<0,i...>(); } }; template<int i, int... j> struct S<i,j...> { static constexpr A gs() { return S<i-1,i,j...>::gs(); } }; constexpr auto X = S<N-1>::gs(); int main() { cout << X[3] << endl; }
这个问题有一个纯粹的C ++ 11(没有提升,也没有macros)解决scheme。 使用与这个答案相同的技巧,我们可以构build一个数字序列,并解压缩它们来调用f
构造一个std::array
:
#include <array> #include <algorithm> #include <iterator> #include <iostream> template<int ...> struct seq { }; template<int N, int ...S> struct gens : gens<N-1, N-1, S...> { }; template<int ...S> struct gens<0, S...> { typedef seq<S...> type; }; constexpr int f(int n) { return n; } template <int N> class array_thinger { typedef typename gens<N>::type list; template <int ...S> static constexpr std::array<int,N> make_arr(seq<S...>) { return std::array<int,N>{{f(S)...}}; } public: static constexpr std::array<int,N> arr = make_arr(list()); }; template <int N> constexpr std::array<int,N> array_thinger<N>::arr; int main() { std::copy(begin(array_thinger<10>::arr), end(array_thinger<10>::arr), std::ostream_iterator<int>(std::cout, "\n")); }
(用g ++ 4.7testing)
你可以完全跳过std::array
做更多的工作,但是我认为在这个例子中使用std::array
更简单。
你也可以recursion地做到这一点:
#include <array> #include <functional> #include <algorithm> #include <iterator> #include <iostream> constexpr int f(int n) { return n; } template <int N, int ...Vals> constexpr typename std::enable_if<N==sizeof...(Vals),std::array<int, N>>::type make() { return std::array<int,N>{{Vals...}}; } template <int N, int ...Vals> constexpr typename std::enable_if<N!=sizeof...(Vals), std::array<int,N>>::type make() { return make<N, Vals..., f(sizeof...(Vals))>(); } int main() { const auto arr = make<10>(); std::copy(begin(arr), end(arr), std::ostream_iterator<int>(std::cout, "\n")); }
这可以说比较简单。
Boost.Preprocessor可以帮助你。 然而,限制是你必须使用整数文字,如10
而不是N
(即使是编译时常量):
#include <iostream> #include <boost/preprocessor/repetition/enum.hpp> #define VALUE(z, n, text) f(n) //ideone doesn't support Boost for C++11, so it is C++03 example, //so can't use constexpr in the function below int f(int x) { return x * 10; } int main() { int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) }; //N = 10 std::size_t const n = sizeof(a)/sizeof(int); std::cout << "count = " << n << "\n"; for(std::size_t i = 0 ; i != n ; ++i ) std::cout << a[i] << "\n"; return 0; }
输出( ideone ):
count = 10 0 10 20 30 40 50 60 70 80 90
macros在以下行中:
int const a[] = { BOOST_PP_ENUM(10, VALUE, ~) };
扩展到:
int const a[] = {f(0), f(1), ... f(9)};
更详细的解释是在这里:
- BOOST_PP_ENUM
如果你想让数组存在静态内存中,你可以试试这个:
template<class T> struct id { typedef T type; }; template<int...> struct int_pack {}; template<int N, int...Tail> struct make_int_range : make_int_range<N-1,N-1,Tail...> {}; template<int...Tail> struct make_int_range<0,Tail...> : id<int_pack<Tail...>> {}; #include <array> constexpr int f(int n) { return n*(n+1)/2; } template<class Indices = typename make_int_range<10>::type> struct my_lookup_table; template<int...Indices> struct my_lookup_table<int_pack<Indices...>> { static const int size = sizeof...(Indices); typedef std::array<int,size> array_type; static const array_type& get() { static const array_type arr = {{f(Indices)...}}; return arr; } }; #include <iostream> int main() { auto& lut = my_lookup_table<>::get(); for (int i : lut) std::cout << i << std::endl; }
如果你想要一个数组的本地副本的工作,只需删除&符号。
我稍微扩展了Flexo和Andrew Tomazos的答案,以便用户可以指定计算范围和要评估的函数。
#include <array> #include <iostream> #include <iomanip> template<typename ComputePolicy, int min, int max, int ... expandedIndices> struct ComputeEngine { static const int lengthOfArray = max - min + sizeof... (expandedIndices) + 1; typedef std::array<typename ComputePolicy::ValueType, lengthOfArray> FactorArray; static constexpr FactorArray compute( ) { return ComputeEngine<ComputePolicy, min, max - 1, max, expandedIndices...>::compute( ); } }; template<typename ComputePolicy, int min, int ... expandedIndices> struct ComputeEngine<ComputePolicy, min, min, expandedIndices...> { static const int lengthOfArray = sizeof... (expandedIndices) + 1; typedef std::array<typename ComputePolicy::ValueType, lengthOfArray> FactorArray; static constexpr FactorArray compute( ) { return FactorArray { { ComputePolicy::compute( min ), ComputePolicy::compute( expandedIndices )... } }; } }; /// compute 1/j struct ComputePolicy1 { typedef double ValueType; static constexpr ValueType compute( int i ) { return i > 0 ? 1.0 / i : 0.0; } }; /// compute j^2 struct ComputePolicy2 { typedef int ValueType; static constexpr ValueType compute( int i ) { return i * i; } }; constexpr auto factors1 = ComputeEngine<ComputePolicy1, 4, 7>::compute( ); constexpr auto factors2 = ComputeEngine<ComputePolicy2, 3, 9>::compute( ); int main( void ) { using namespace std; cout << "Values of factors1" << endl; for ( int i = 0; i < factors1.size( ); ++i ) { cout << setw( 4 ) << i << setw( 15 ) << factors1[i] << endl; } cout << "------------------------------------------" << endl; cout << "Values of factors2" << endl; for ( int i = 0; i < factors2.size( ); ++i ) { cout << setw( 4 ) << i << setw( 15 ) << factors2[i] << endl; } return 0; }
这里有不less好的答案。 问题和标签指定了c++11
,但是随着时间的推移,一些(像我自己)在这个问题上磕磕绊绊,可能会开放使用c++14
。 如果是这样,使用std::integer_sequence
可以非常简洁地做到这一点。 此外,它可以用来实例化更长的数组,因为当前的“我最好”受到recursion深度的限制。
constexpr std::size_t f(std::size_t x) { return x*x; } // A constexpr function constexpr std::size_t N = 5; // Length of array using TSequence = std::make_index_sequence<N>; static_assert(std::is_same<TSequence, std::integer_sequence<std::size_t, 0, 1, 2, 3, 4>>::value, "Make index sequence uses std::size_t and produces a parameter pack from [0,N)"); using TArray = std::array<std::size_t,N>; // When you call this function with a specific std::integer_sequence, // the parameter pack i... is used to deduce the the template parameter // pack. Once this is known, this parameter pack is expanded in // the body of the function, calling f(i) for each i in [0,N). template<std::size_t...i> constexpr TArray get_array(std::integer_sequence<std::size_t,i...>) { return TArray{{ f(i)... }}; } int main() { constexpr auto s = TSequence(); constexpr auto a = get_array(s); for (const auto &i : a) std::cout << i << " "; // 0 1 4 9 16 return EXIT_SUCCESS; }
这里有一个更简洁的答案,你明确地声明原始序列中的元素。
#include <array> constexpr int f(int i) { return 2 * i; } template <int... Ts> struct sequence { using result = sequence<f(Ts)...>; static std::array<int, sizeof...(Ts)> apply() { return {{Ts...}}; } }; using v1 = sequence<1, 2, 3, 4>; using v2 = typename v1::result; int main() { auto x = v2::apply(); return 0; }
这个怎么样?
#include <array> #include <iostream> constexpr int f(int i) { return 2 * i; } template <int N, int... Ts> struct t { using type = typename t<N - 1, Ts..., 101 - N>::type; }; template <int... Ts> struct t<0u, Ts...> { using type = t<0u, Ts...>; static std::array<int, sizeof...(Ts)> apply() { return {{f(Ts)...}}; } }; int main() { using v = typename t<100>::type; auto x = v::apply(); }