12

Essentially, the situation is as follows:

I have a class template (using one template parameter length of type int) and want to introduce a static array. This array should be of length length and contain the elements 1 to length.

The code looks as follows up to now:

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

Then I wanted to write a line for initalizing the array

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

(How) can this be achieved?

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
phimuemue
  • 34,669
  • 9
  • 84
  • 115
  • I asked a similar question some time ago: http://stackoverflow.com/questions/2850646/fill-container-with-template-parameters . But with `std::tr1::array` instead of this C-style array... – phlipsy Jul 01 '10 at 12:09

9 Answers9

5

You can't do that with C-style arrays because they don't have value semantics.

If you use something like std::tr1::array however then you could easily do what you want by initialising to a function result, or by using an iterator that generates those values.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
  • Boost.assign is very helpful for initializing any sort of container: http://www.boost.org/doc/libs/1_43_0/libs/assign/doc/index.html – Björn Pollex Jul 01 '10 at 12:10
2

Use "static constructor" idiom.

// 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];
    }
}
adf88
  • 4,277
  • 1
  • 23
  • 21
  • "static constructor idiom" gives 0 results ;) Can't be much of an idiom. – Peter Alexander Jul 01 '10 at 12:07
  • 1
    Sorry but this doesn't work for me under gcc-4.3, the static constructor never gets called... Besides the fact that we need `myClass::StaticConstructor myClass::static_constructor`. – phlipsy Jul 01 '10 at 13:34
  • Can we safely assume that array will be initialized before static_constructor? I'm not sure if static data members have a defined initialization order (as opposed to non-static members which do). It seems like they should be initialized in the order they are declared in the class definition, however... – stinky472 Jul 01 '10 at 13:41
  • @stinky472: Within the same translation unit, statics are instantiated in the order in which they appear. It's only between translation units that makes them undefined. – Puppy Jul 01 '10 at 18:47
  • Yes this can't work that way. You need to ensure that `static_constructor` is always referenced. Otherwise when the user only reerences `array`, it won't be filled. You also have some bugs regarding template usage in your code. Please test your answer if you aren't sure about it. – Johannes Schaub - litb Jul 01 '10 at 23:57
  • It was just a concept. Anyway, I corrected the example and now it's working. – adf88 Jul 02 '10 at 08:13
1

You can write a wrapper class, but I'm sure there are cleaner solutions:

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;
};
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
1

It seems tough. The closest approach that i can think of would be the following :

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;
Benoît
  • 16,798
  • 8
  • 46
  • 66
1

Can't you wrap the array in a static function, so for example,

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

and then access it like,

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

It will be initialised on first use, and on following accesses since initd is static, if(!initd) will be false and the initialisation step will be skipped.

tjm
  • 7,500
  • 2
  • 32
  • 53
1

I think this only works in C++0x. In C++03 whatever you do - you will end up with a dynamically initialized array, and thus potentially have initialization order problems. The following C++0x code won't have such problems.

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;
};
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
0

embed a for loop in a static constructor that runs up to length, its basically the same as using the initializer:

for(int i = 0; i < length; i++)
    array[i] = i + 1;
Community
  • 1
  • 1
Necrolis
  • 25,836
  • 3
  • 63
  • 101
0

Here is an example using 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;
  }
}

Note that the array is larger than length; currently its size is fixed.

Philipp
  • 48,066
  • 12
  • 84
  • 109
0

You can use explicit template instantiation of an additional static member whose constructor takes care of filling out the entries:

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, since a similar (probably better) solution has been already shown above by Benoit, here's a template recursive version, just for fun:

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

Now, different compilers support different levels of template recursion (and this technique is compiler expensive) so, careful..."Here Be Dragons" ;-)

Oh, one more thing, you don't need to redefine the array in the specialized version of myClass, so you can get rid of instantiating 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;
    }
};
blue scorpion
  • 387
  • 1
  • 10