Maak N-element constexpr array in C++11

Hallo, ik ben C++11 aan het leren, ik vraag me af hoe ik een constexpr 0 tot n array kan maken, bijvoorbeeld:

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

array kan dus {0, 1, 2, 3, 4, 5}

zijn


Antwoord 1, autoriteit 100%

In C++14 kan het eenvoudig worden gedaan met een constexpr-constructor en een lus:

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

Antwoord 2, autoriteit 100%

In tegenstelling tot de antwoorden in de opmerkingen op uw vraag, kunt u dit doen zonder compiler-extensies.

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

Antwoord 3, autoriteit 61%

Gebaseerd op @Xeo’s uitstekende idee, is hier een aanpak waarmee u een scala aan

kunt vullen

  • constexpr std::array<T, N> a = { fun(0), fun(1), ..., fun(N-1) };
  • waarbij Teen letterlijk type is (niet alleen intof andere geldige niet-type sjabloonparametertypen), maar ook double, of std::complex(vanaf C++14 en later)
  • waarbij fun()een constexpr-functie is
  • die wordt ondersteund door std::make_integer_sequencevanaf C++14, maar vandaag eenvoudig kan worden geïmplementeerd met zowel g++ als Clang (zie Live-voorbeeld aan het einde van het antwoord)
  • Ik gebruik de implementatie bij GitHub-licentievan @JonathanWakely )

Hier is de 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 voorbeeld


Antwoord 4, autoriteit 11%

Gebruik C++14 integral_sequence, of zijn invariante 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:
U kunt std::array:

. gebruiken

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

Antwoord 5, autoriteit 2%

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

Antwoord 6

Het gebruik van boost-preprocessor is heel eenvoudig:

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

UITGANG:

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

Antwoord 7

Overweeg boost::mpl::range_c<int, 0, N>in plaats daarvan.

Other episodes