2010-07-01 41 views
12

本質上,情況如下:C++:根據模板參數填充陣列

我有一個類模板(使用int類型的一個模板參數length)和要介紹一個靜態數組。該陣列的長度應爲length,幷包含元素1length

代碼看起來和現在一樣瞭如下方式:

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

然後我想寫一個線initalizing陣列

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

(如何)才能實現這一目標?

+0

前段時間我問了一個類似的問題:http://stackoverflow.com/questions/2850646/fill-container-with-template-parameters。但是用'std :: tr1 :: array'代替這個C風格的數組...... – phlipsy 2010-07-01 12:09:28

回答

2

使用「靜態構造函數」習語。

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

「static constructor idiom」給出0個結果;)不能成爲一個成語。 – 2010-07-01 12:07:25

+1

http://www.google.com/search?q="static+constructor"+c%2B%2B – adf88 2010-07-01 12:12:33

+1

對不起,但這不適用於我在gcc-4.3下,靜態構造函數永遠不會被調用...除了我們需要'myClass :: StaticConstructor myClass :: static_constructor'的事實。 – phlipsy 2010-07-01 13:34:25

5

你不能做到這一點與C風格的陣列,因爲他們沒有價值的語義。

但是,如果您使用類似std::tr1::array的東西,那麼您可以通過初始化函數結果或使用生成這些值的迭代器輕鬆完成您想要的操作。

+0

+1。這裏需要某種類來做到這一點。 – stinky472 2010-07-01 12:07:21

+0

Boost.assign對初始化任何類型的容器非常有幫助:http://www.boost.org/doc/libs/1_43_0/libs/assign/doc/index.html – 2010-07-01 12:10:56

1

你可以寫一個包裝類,但我敢肯定有更清潔的解決方案:

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

嵌入在一個static constructor循環運行的最大長度,其基本相同,使用初始化:

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

看起來很難。我能想到的最接近的方法是下列:

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

如果您未實例化myClass,會發生什麼情況? – fredoverflow 2010-07-01 12:13:44

+0

沒有我剛剛編輯的修改,它不會工作。 – 2010-07-01 12:17:56

0

下面是使用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; 
    } 
} 

注意,陣列比length大;目前它的大小是固定的。

0

您可以使用一個額外的靜態成員,其構造函數負責填寫條目的明確模板實例:

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

或者,因爲類似的(可能更好)的解決方案上面已經由伯努瓦表示,這裏有一個模板遞歸版本,只是爲了好玩:

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

現在,不同的編譯器支持不同級別的模板遞歸(這個技術是編譯器價格昂貴),所以,小心...「在這裏龍」 ;-)

哦,還有一件事,你並不需要重新定義數組中myClass相關聯的特殊版本,這樣你就可以擺脫實例數組[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

你能不能換陣列中的靜態函數,因此,例如,

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

,然後訪問它像,

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

它將在第一次使用時初始化,並且由於initd是靜態的,所以在下面的訪問中,if(!initd)將是錯誤的,並且初始化步驟將被跳過。

1

我認爲這隻適用於C++ 0x。在C++ 03中,無論你做什麼 - 你最終都會得到一個動態初始化的數組,因此可能有初始化順序問題。以下C++ 0x代碼不會有這樣的問題。

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