2013-09-26 11 views
40

Ciao sto imparando C++ 11, mi chiedo come fare un constexpr 0 a n array, ad esempio:Crea N-elemento dell'array constexpr in C++ 11

n = 5; 

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

così serie può essere {0, 1, 2, 3, 4, 5}

+2

[Ecco una domanda simile recente] (http://stackoverflow.com/q/19016099/420683) utilizzando 'std :: array's. – dyp

risposta

41

A differenza di quelle risposte nei commenti alla tua domanda, puoi farlo senza estensioni del compilatore.

#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 
} 
+0

@Kal nice, ma "only" funziona per 'int' o altri tipi che possono apparire come parametri di template non di tipo (quindi non per esempio' double'). Vedi la mia risposta per la soluzione generale. – TemplateRex

+0

Bello. Ma penso che nasconderei il modello variadic ricorsivo dietro un'interfaccia pubblica non variata, per evitare confusione se qualcuno prova 'Array <9,3,5>'. – aschepler

4

Usare C++ 14 integral_sequence, o la sua index_sequence invariante

#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: È possibile utilizzare 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; 
} 
+1

quale compilatore supporta già questo? – TemplateRex

+0

Clang + libC++ supporto dell'albero di supporto. Passa '-std = C++ 1y'. –

+0

Uso Clang 3.3 e GCC 4.8.1 con opzioni -std = C++ 11. –

25

Sulla base @ Xeo di excellent idea, ecco un approccio che ti consente di riempire un array di

  • constexpr std::array<T, N> a = { fun(0), fun(1), ..., fun(N-1) };
  • dove T è qualsiasi tipo letterale (non solo int o di altro tipo non-tipo dei parametri di modello valido), ma anche double, o std::complex (da C++ 14 in poi)
  • dove fun() è qualsiasi constexpr funzione
  • che è supportato da std::make_integer_sequence da C++ 14 in avanti, ma facilmente implementato oggi sia con g ++ e Clang (vedi Esempio vivo alla fine della risposta)
  • Io uso @JonathanWakely s' implementation at GitHub (Boost licenza)

Ecco il codice

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

+0

'dove auto è qualsiasi tipo letterale' Non è un 'std :: array' di tipi letterali, in' auto a = make_array (fun); ', equivalente a' auto a = std :: array {fun (0), fun (1), ..}; '? Inoltre, l'esempio 'constexpr auto a = {1,2};' deduce 'a' come una' std :: initializer_list', che non è ancora richiesta per essere un tipo letterale (-> no 'constexpr'). (So ​​che è piuttosto pedante, ma a prima vista ero confuso.) – dyp

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

usare il boost preprocessore, è molto semplice.

#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

+0

Penso che potresti anche usare una delle macro 'ENUM', ad es. 'BOOST_PP_ENUM_PARAMS (25, BOOST_PP_EMPTY())', invece di 'REPEAT' +' COMMA_IF' – dyp

14

In C++ 14 può essere fatto facilmente con un constexpr costruttore e un ciclo:

#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'; 
} 
+0

questo è conciso. Perché '' arr() 'è necessario? – IceFire

+1

@IceFire probabilmente perché non è possibile avere campi non inizializzati nella funzione 'constexpr' (che qui è un costruttore). Solo una supposizione. – Abyx

0

Prova boost::mpl::range_c<int, 0, N>docs.