C ++: Remplissage réseau en fonction du paramètre de modèle
Question
Pour l'essentiel, la situation se présente comme suit:
J'ai un modèle de classe (en utilisant un paramètre modèle length
de type int
) et que vous souhaitez introduire un tableau statique. Ce tableau devrait être de length
de longueur et contiennent les éléments 1
à length
.
Les regards de code comme suit jusqu'à présent:
template<int length>
class myClass{
static int array[length];
};
Alors je voulais écrire une ligne pour initalizing le tableau
// of course, the line below does not work as intended.
template<int length> int myClass<length>::array[length]={1,2, ..., length};
(Comment) peut-il être atteint?
La solution
Utilisez expression "constructeur statique".
// 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];
}
}
Autres conseils
Vous ne pouvez pas le faire avec des tableaux de style C car ils n'ont pas la sémantique de valeur.
Si vous utilisez quelque chose comme std::tr1::array
mais vous pouvez facilement faire ce que vous voulez en initialisant à un résultat de fonction, ou en utilisant un itérateur qui génère ces valeurs.
Vous pouvez écrire une classe wrapper, mais je suis sûr qu'il ya des solutions plus propres:
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;
};
Il semble difficile. L'approche la plus proche que je peux penser serait le suivant:
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;
ne peut pas vous envelopper le tableau dans une fonction statique, par exemple,
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;
};
};
et accéder ensuite comme,
myClass<4>::myArray()[2] = 42;
Il sera initialisés sur la première utilisation, et suivant les accès depuis initd
est statique, if(!initd)
sera faux et l'étape d'initialisation est sautée.
Je pense que cela ne fonctionne que dans C ++ 0x. En C ++ 03 quoi que vous fassiez - vous finirez avec un tableau initialisé dynamiquement, et donc potentiellement des problèmes d'ordre d'initialisation. Le code suivant C ++ 0x aura pas de tels problèmes.
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;
};
intégrer une boucle dans un constructeur statique qui court jusqu'à la longueur, son fondamentalement la même que l'utilisation du initialiseur:
for(int i = 0; i < length; i++)
array[i] = i + 1;
Voici un exemple en utilisant 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;
}
}
Notez que le tableau est plus grand que length
; actuellement sa taille est fixe.
Vous pouvez utiliser instanciation explicite de modèle d'un membre statique supplémentaire dont le constructeur prend soin de remplir les entrées:
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;
}
Ou, comme une solution similaire (probablement mieux) a déjà été indiqué ci-dessus par Benoit, voici une version récursive de modèle, juste pour le plaisir:
//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;
}
Maintenant, différents compilateurs prennent en charge différents niveaux de récursivité modèle (et cette technique est compilateur cher) donc, attention ... "Here Be Dragons"; -)
Oh, encore une chose, vous n'avez pas besoin de redéfinir le tableau dans la version spécialisée de myClass, de sorte que vous pouvez vous débarrasser de tableau instanciation [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;
}
};