Create N-element constexpr array in C++11

Hello i'm learning C++11, I'm wondering how to make a constexpr 0 to n array, for example:

n = 5;


int array[] = {0 ... n};

so array may be {0, 1, 2, 3, 4, 5}

69383 次浏览

Unlike those answers in the comments to your question, you can do this without compiler extensions.

#include <iostream>


template<int N, int... Rest>
struct Array_impl {
static constexpr auto& value = Array_impl<N - 1, N, Rest...>::value;
};


template<int... Rest>
struct Array_impl<0, Rest...> {
static constexpr int value[] = { 0, Rest... };
};


template<int... Rest>
constexpr int Array_impl<0, Rest...>::value[];


template<int N>
struct Array {
static_assert(N >= 0, "N must be at least 0");


static constexpr auto& value = Array_impl<N>::value;


Array() = delete;
Array(const Array&) = delete;
Array(Array&&) = delete;
};


int main() {
std::cout << Array<4>::value[3]; // prints 3
}

Use C++14 integral_sequence, or its invariant index_sequence

#include <iostream>


template< int ... I > struct index_sequence{


using type = index_sequence;
using value_type = int;


static constexpr std::size_t size()noexcept{ return sizeof...(I); }
};


// making index_sequence
template< class I1, class I2> struct concat;


template< int ...I, int ...J>
struct concat< index_sequence<I...>, index_sequence<J...> >
:  index_sequence< I ... , ( J + sizeof...(I) )... > {};


template< int N > struct make_index_sequence_impl;


template< int N >
using make_index_sequence = typename make_index_sequence_impl<N>::type;


template< > struct make_index_sequence_impl<0> : index_sequence<>{};
template< > struct make_index_sequence_impl<1> : index_sequence<0>{};


template< int N > struct make_index_sequence_impl
: concat< make_index_sequence<N/2>, make_index_sequence<N - N/2> > {};






// now, we can build our structure.
template < class IS > struct mystruct_base;


template< int ... I >
struct mystruct_base< index_sequence< I ... > >
{


static constexpr int array[]{I ... };
};


template< int ... I >
constexpr int mystruct_base< index_sequence<I...> >::array[] ;


template< int N > struct mystruct
: mystruct_base< make_index_sequence<N > >
{};


int main()
{
mystruct<20> ms;


//print
for(auto e : ms.array)
{
std::cout << e << ' ';
}
std::cout << std::endl;


return 0;
}


output: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

UPDATE: You may use std::array:

template< int ... I >
static constexpr std::array< int, sizeof...(I) >  build_array( index_sequence<I...> ) noexcept
{
return std::array<int, sizeof...(I) > { I... };
}


int main()
{
std::array<int, 20> ma = build_array( make_index_sequence<20>{} );


for(auto e : ma) std::cout << e << ' ';
std::cout << std::endl;
}

Based on @Xeo's excellent idea, here is an approach that lets you fill an array of

  • constexpr std::array<T, N> a = { fun(0), fun(1), ..., fun(N-1) };
  • where T is any literal type (not just int or other valid non-type template parameter types), but also double, or std::complex (from C++14 onward)
  • where fun() is any constexpr function
  • which is supported by std::make_integer_sequence from C++14 onward, but easily implemented today with both g++ and Clang (see Live Example at the end of the answer)
  • I use @JonathanWakely 's implementation at GitHub (Boost License)

Here is the code

template<class Function, std::size_t... Indices>
constexpr auto make_array_helper(Function f, std::index_sequence<Indices...>)
-> std::array<typename std::result_of<Function(std::size_t)>::type, sizeof...(Indices)>
{
return \{\{ f(Indices)... }};
}


template<int N, class Function>
constexpr auto make_array(Function f)
-> std::array<typename std::result_of<Function(std::size_t)>::type, N>
{
return make_array_helper(f, std::make_index_sequence<N>{});
}


constexpr double fun(double x) { return x * x; }


int main()
{
constexpr auto N = 10;
constexpr auto a = make_array<N>(fun);


std::copy(std::begin(a), std::end(a), std::ostream_iterator<double>(std::cout, ", "));
}

Live Example

#include <array>
#include <iostream>


template<int... N>
struct expand;


template<int... N>
struct expand<0, N...>
{
constexpr static std::array<int, sizeof...(N) + 1> values = \{\{ 0, N... }};
};


template<int L, int... N> struct expand<L, N...> : expand<L-1, L, N...> {};


template<int... N>
constexpr std::array<int, sizeof...(N) + 1> expand<0, N...>::values;


int main()
{
std::cout << expand<100>::values[9];
}

Using boost preprocessor, it's very simple:

 #include <cstdio>
#include <cstddef>


#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>


#define IDENTITY(z,n,dummy)   BOOST_PP_COMMA_IF(n) n


#define INITIALIZER_n(n)   { BOOST_PP_REPEAT(n,IDENTITY,~)  }


int main(int argc, char* argv[])
{
int array[] = INITIALIZER_n(25);


for(std::size_t i = 0; i < sizeof(array)/sizeof(array[0]); ++i)
printf("%d ",array[i]);


return 0;
}

OUTPUT:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

In C++14 it can be easily done with a constexpr constructor and a loop:

#include <iostream>


template<int N>
struct A {
constexpr A() : arr() {
for (auto i = 0; i != N; ++i)
arr[i] = i;
}
int arr[N];
};


int main() {
constexpr auto a = A<4>();
for (auto x : a.arr)
std::cout << x << '\n';
}