Create N-element constexpr array in C++11

60,056

Solution 1

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';
}

Solution 2

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
}

Solution 3

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

Solution 4

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;
}

Solution 5

#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];
}
Share:
60,056
Admin
Author by

Admin

Updated on July 05, 2022

Comments

  • Admin
    Admin almost 2 years

    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}

  • TemplateRex
    TemplateRex over 10 years
    @Kal nice, but "only" works for int or other types that can appear as non-type template parameters (so not for e.g. double). See my answer for the general solution.
  • Sebastian Redl
    Sebastian Redl over 10 years
    Clang + libc++ tip of tree support this. Pass -std=c++1y.
  • Khurshid Normuradov
    Khurshid Normuradov over 10 years
    I use Clang 3.3 and GCC 4.8.1 with -std=c++11 options.
  • aschepler
    aschepler over 10 years
    Nice. But I think I would hide the recursive variadic template behind a non-variadic public interface, to avoid confusion if somebody tries Array<9,3,5>.
  • dyp
    dyp over 10 years
    where auto is any literal type Isn't it a std::array of literal types, in auto a = make_array<N>(fun);, equivalent to auto a = std::array<decltype(fun(0)), N>{fun(0), fun(1), ..};? Also, the example constexpr auto a = {1,2}; deduces a to be a std::initializer_list, which isn't yet required to be a literal type (-> no constexpr). (I know it's rather pedantic, but I was confused at first glance.)
  • dyp
    dyp over 10 years
    I think you could also use one of the ENUM macros, e.g. BOOST_PP_ENUM_PARAMS(25, BOOST_PP_EMPTY()), instead of the REPEAT+COMMA_IF
  • IceFire
    IceFire about 8 years
    this is concise. Why is : arr() necessary?
  • Abyx
    Abyx about 8 years
    @IceFire probably because you cannot have uninitialized fields in constexpr function (which is a constructor here). Just a guess.
  • Romeo Valentin
    Romeo Valentin almost 5 years
    How could you pass a function to A that would be applied to all array elements? When using template <int N, class Function> ... constexpr A(Function f) : arr() {... I can't figure out how to instantiate the struct.
  • jjcf89
    jjcf89 over 4 years
    Note: doesn't work with Visual Studio 15, might be supported in Visual Studio 2017.
  • Marcin K.
    Marcin K. about 4 years
    I cant get this to compile. I am also using Visual Studio 15. Can someone confirm that it is valid in 2017+?
  • Kyrion
    Kyrion almost 3 years
    This works fine for me with C++17 in Visual Studio 2019. Regarding a short experiment on godbolt, the example also compiles with the oldest msvc toolchain 19.14 available there, which seems to be shipped with Visual Studio 2017.