88

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}

2
  • 3
    Here's a recent similar question using std::arrays. Commented Sep 26, 2013 at 9:45
  • We're at C++17 now. So, @abyx 's answer is now the best answer, because it can no longer be considered to be dependent on "compiler extensions". Commented Apr 22, 2019 at 17:04

9 Answers 9

75

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';
}
Sign up to request clarification or add additional context in comments.

7 Comments

this is concise. Why is : arr() necessary?
@IceFire probably because you cannot have uninitialized fields in constexpr function (which is a constructor here). Just a guess.
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.
Note: doesn't work with Visual Studio 15, might be supported in Visual Studio 2017.
Great answer and this work nicely for me. Just wondering - in the newer versions of C++ are there any new syntactical ways of doing this?
|
58

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
}

2 Comments

@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.
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>.
40

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

1 Comment

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.)
6

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

2 Comments

Clang + libc++ tip of tree support this. Pass -std=c++1y.
I use Clang 3.3 and GCC 4.8.1 with -std=c++11 options.
3

For std::array in C++17, constexpr function are also accepted Note that the var 'arr' must be initialized by constexpr required.
(initialize: same meaning with answer of @abyx)

#include <array>
constexpr std::array<int, 3> get_array()
{
    std::array<int, 3> arr{0};
    arr[0] = 9;
    return arr;
}
static_assert(get_array().size() == 3);

Comments

2

With C++17 this can be done easily as std::array::begin is marked constexpr.

template<std::size_t N> std::array<int, N + 1> constexpr make_array()
{
    std::array<int, N + 1> tempArray{};
    int count = 0;
    for(int &elem:tempArray)
    {
        elem = count++;
    }
    return tempArray;
}
int main()
{
    
    //-------------------------------vv------>pass the size here 
    constexpr auto arr  = make_array<5>();
   
    //lets confirm if all objects have the expected value 
    for(const auto &elem: arr)
    {
        std::cout << elem << std::endl; //prints 1 2 3 4 5 with newline in between
    }
    
}

Demo

Comments

1
#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];
}

Comments

-2

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

1 Comment

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
-6

Consider boost::mpl::range_c<int, 0, N> instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.