2010-07-01 15 views
12

Zasadniczo sytuacja przedstawia się następująco:C++: Wypełnij tablicę według parametru szablonu

Mam szablonu klasy (przy użyciu parametru jeden szablon length typu int) i chcą wprowadzić statyczną tablicę. Ta tablica powinna mieć długość length i zawierać elementy od 1 do length.

Kod wygląda następująco aż do teraz:

template<int length> 
class myClass{ 
    static int array[length]; 
}; 

Potem chciałem napisać wiersz dla initalizing tablicę

// of course, the line below does not work as intended. 
template<int length> int myClass<length>::array[length]={1,2, ..., length}; 

(Jak) można to osiągnąć?

+0

Zadałem podobne pytanie jakiś czas temu: http://stackoverflow.com/questions/2850646/fill-container-with-template-parameters. Ale z 'std :: tr1 :: array' zamiast tej tablicy w stylu C ... – phlipsy

Odpowiedz

2

Użyj "idiomu statycznego".

// EDIT 2

#include <iostream> 

template<int length> 
class myClass { 
public: 
    typedef int ArrayType[length]; 

    static struct StaticData { 
     ArrayType array; 

     StaticData() 
     { 
      for (int i = 0; i < length; i++) array[i] = i; 
     } 
    } 
    static_data; 

    static ArrayType &array; 
}; 

template<int length> 
typename myClass<length>::StaticData myClass<length>::static_data; 

template<int length> 
typename myClass<length>::ArrayType &myClass<length>::array = myClass<length>::static_data.array; 

int main(int argc, char** argv) { 
    const int LEN = 5; 
    for (int i = 0; i < LEN; i++) { 
     std::cout << myClass<LEN>::array[i]; 
    } 
} 
+0

" Statyczny idiom konstruktora "daje 0 wyników;) Nie może być dużo idiomu. –

+1

http://www.google.com/search?q="static+constructor"+c%2B%2B – adf88

+1

Przepraszam, ale to nie działa dla mnie pod gcc-4.3, statyczny konstruktor nigdy nie zostanie wywołany ... Poza tym, że potrzebujemy 'myClass :: StaticConstructor myClass :: static_constructor'. – phlipsy

5

Nie można tego zrobić z tablicami w stylu C, ponieważ nie mają semantyki wartości.

Jeśli jednak używasz czegoś takiego jak std::tr1::array, możesz łatwo zrobić to, co chcesz, inicjując wynik funkcji lub używając iteratora generującego te wartości.

+0

+1. Potrzebujesz tutaj jakiejś klasy. – stinky472

+0

Funkcja Boost.assign jest bardzo pomocna przy inicjowaniu wszelkiego rodzaju kontenerów: http://www.boost.org/doc/libs/1_43_0/libs/assign/doc/index.html –

1

Można napisać klasy otoki, ale jestem pewien, że są czystsze rozwiązania:

template <size_t length> 
class array_init_1_to_n 
{ 
    int array[length]; 

public: 

    array_init_1_to_n() 
    { 
     for (int i = 0; i < length; ++i) 
     { 
      array[i] = i + 1; 
     } 
    } 

    operator int*() 
    { 
     return array; 
    } 

    operator const int*() const 
    { 
     return array; 
    } 
}; 

template<size_t length> 
class myClass{ 
    static array_init_1_to_n<length> array; 
}; 
0

osadzić pętli for w static constructor która biegnie aż do długości, jego w zasadzie takie same, jak przy użyciu inicjatora:

for(int i = 0; i < length; i++) 
    array[i] = i + 1; 
1

Wydaje się trudne. Najbliższy podejście, że mogę myśleć byłoby następujące:

template<int length> 
class myClass 
{ 
    public: 
    myClass() 
    { 
     static InitializeArray<length> initializeArray(&array); 
    } 
    template<int length> 
    class InitializeArray 
    { 
    public: 
     InitializeArray(int* array) 
     { 
     for(int i = 0; i < length ; ++i) 
     array[i] = i; 
     } 
    }; 
    static int array[length]; 
    static myClass instance; 
}; 
template<int length> int myClass<length>::array[length]; 
template<int length> myClass myClass::instance; 
+0

Co się stanie, jeśli nie utworzysz instancji myClass? – fredoverflow

+0

Bez modyfikacji, którą właśnie edytowałem, nie zadziała. –

0

Oto przykład przy użyciu Boost.MPL:

#include <cstddef> 
#include <iostream> 

#include <boost/mpl/range_c.hpp> 
#include <boost/mpl/string.hpp> 

template<std::size_t length> 
struct myClass { 
    static const std::size_t Length = length; 
    typedef typename boost::mpl::c_str< boost::mpl::range_c<std::size_t, 1, length + 1> > Array; 
}; 

int main() { 
    // check whether the array really contains the indented values 
    typedef myClass<10> test; 
    for (std::size_t i = 0; i < test::Length; ++i) { 
    std::cout << test::Array::value[i] << std::endl; 
    } 
} 

Zauważ, że tablica jest większa niż length; obecnie jego rozmiar jest naprawiony.

0

Można użyć wyraźny szablonu konkretyzacji dodatkowego członka statycznego, którego konstruktor dba o wypełnienie wpisy:

template<int length> 
class myClass{ 
public: 
    static int array[length]; 

    typedef enum{LENGTH=length} size_; 

    struct filler 
    { 
     filler(void) 
     { 
      for(int i=0;i<LENGTH;++i) 
       array[i]=i+1; 
     } 
    }; 

    static filler fill_; 
}; 

// of course, the line[s] below now do work as intended. 
template<int length> 
int myClass<length>::array[length]; 

//static member definition 
template<int length> 
typename myClass<length>::filler myClass<length>::fill_; 

//explicit template instantiation 
template myClass<5>::filler myClass<5>::fill_; 

int main(void) 
{ 
    for(int i=0;i<myClass<5>::LENGTH;++i) 
     cout<<myClass<5>::array[i]<<endl; 

    return 0; 
} 

OR, ponieważ podobny (prawdopodobnie lepsze) rozwiązanie zostało już przedstawione powyżej przez Benoit, oto szablon rekurencyjna wersja, po prostu dla zabawy:

//recursive version: 
template<int length> 
class myClass{ 
public: 
    static int array[length]; 

    typedef enum{LENGTH=length} size_; 

    static void do_fill(int* the_array) 
    { 
     the_array[LENGTH-1]=LENGTH; 
     myClass<length-1>::do_fill(the_array); 
    } 

    struct filler 
    { 
     filler(void) 
     { 
      /*for(int i=0;i<LENGTH;++i) 
       array[i]=i+1;*/ 
      do_fill(array); 
     } 
    }; 

    static filler fill_; 
}; 

//explicit specialization to end the recursion 
template<> 
class myClass<1>{ 
public: 
    static int array[1]; 

    typedef enum{LENGTH=1} size_; 

    static void do_fill(int* the_array) 
    { 
     the_array[LENGTH-1]=LENGTH; 
    } 
}; 

//definition of the explicitly specialized version of the array 
//to make the linker happy: 
int myClass<1>::array[1]; 

// of course, the line below does not work as intended. 
template<int length> 
int myClass<length>::array[length]; 

//static member definition 
template<int length> 
typename myClass<length>::filler myClass<length>::fill_; 

//explicit template instantiation 
template myClass<5>::filler myClass<5>::fill_; 

int main(void) 
{ 
    for(int i=0;i<myClass<5>::LENGTH;++i) 
     cout<<myClass<5>::array[i]<<endl; 

    return 0; 
} 

teraz różne kompilatory obsługują różne poziomy szablonu rekursji (a technika ta jest kompilator drogie) tak, staranne ...„Here Be Dragons” ;-)

Aha, jeszcze jedno, nie trzeba na nowo zdefiniować tablicę w wyspecjalizowanych wersji myClass, więc można pozbyć instancji array [1]:

//explicit specialization to end the recursion 
template<> 
class myClass<1>{ 
public: 
    typedef enum{LENGTH=1} size_; 

    static void do_fill(int* the_array) 
    { 
     the_array[LENGTH-1]=LENGTH; 
    } 
}; 
0

nie można zawinąć tablicy w funkcji statycznej, więc na przykład

template<int length> 
class myClass { 
    static int* myArray() { 
     static bool initd = false; 
     static int array[length]; 
     if(!initd) { 
      for(int i=0; i<length; ++i) { 
       array[i] = i+1; 
      } 
      initd = true; 
     } 
     return array; 
    }; 
}; 

a następnie dostęp do niego podobny,

myClass<4>::myArray()[2] = 42; 

Zostanie zainicjowany przy pierwszym użyciu, a po kolejnych dostępach od initd jest statyczny, if(!initd) będzie fałszywy, a krok inicjalizacji zostanie pominięty.

1

Myślę, że działa to tylko w C++ 0x. W C++ 03 cokolwiek robisz - otrzymasz dynamicznie zainicjowaną tablicę, a tym samym potencjalnie problemy z kolejnością inicjalizacji. Poniższy kod 0x ++ nie będzie miał takich problemów.

template<int...> 
struct myArray; 

template<int N, int ...Ns> 
struct myArray<N, Ns...> : myArray<N-1, N, Ns...> { }; 

template<int ...Ns> 
struct myArray<0, Ns...> { 
    static int array[sizeof...(Ns)]; 
}; 

template<int ...Ns> 
int myArray<0, Ns...>::array[sizeof...(Ns)] = { Ns... } ; 

template<int length> 
class myClass : myArray<length> { 
    using myArray<length>::array; 
}; 
Powiązane problemy