2009-07-14 11 views
3

次のようなテンプレートであるn次元配列クラスを実装しています(データは長さがすべての次元の積である線形配列に格納されています):n次元配列のコンストラクタのテンプレート仕様

template< class valType, int rank > 
class NDimensionalArray 
{ 
public: 

private: 
    valType* m_data; 
    int* m_dimensions; 
    int m_rank; 
}; 

だから、アイデアは、ユーザー(私は)すなわち、ランク2の配列を指定して、特定の次元のことができるということです。

NDimensionalArray<double,2> matrix(10,10); 

今難易1-用のコンストラクタを専門にしています> n次元では、各コンストラクタはn個のパラメータをとり、nはarの階数レイ。今のprintfで使用されているように私は()のvalarrayを使用して考えるが、これは2つの寸法を有する1次元アレイを定義すると、すなわち:

NDimensionalArray<double,1> matrix(10,10); 

は完全に許容される行動であろう。コンパイラに繰り返しをさせるために使うことができる素敵なトリックはありますか?現実的にあまりにも長い間、私はランクを知っており、各次元の長さを持っているとして、コンストラクタは、一般的なことができます:

{ 
    int nElements = m_dimensions[0]; 
    for (int i=1 ; i<m_rank ; ++i) 
     nElements *= m_dimensions[i]; 
    m_data = new valType[nElements]; 
} 

編集:同様の操作をアクセサのために必要とされるであろうこと注意。

また、私がどのように見えるのコンストラクタのオプションを検討している:

のように使用することができ
NDimensionalArray(const NDimensionalArray<int,1>& dimensions); 

:に比べてこれは実行可能なソリューションが、その醜いだろう

NDimensionalArray<int,1> dimVec(2); // Need a specification for 1-dimensional arrays. 
dimVec(0) = 10; 
dimVec(1) = 10; 
NDimensionalArray<double,2> matrix(dimVec); 

私が望む使い方。また、多次元配列にアクセスすることは深刻な苦痛となり、アクセスごとに次元ベクトルを構築する必要があります。

+2

は、 '後押ししません::あなたがすべてで、このようなクラスを記述する必要なく正確にあなたがやりたいmulti_array'? –

+1

それは確かにでしょう。それはもっと「私はそれをどうやってできるのだろう」というプロジェクトでした。あなたが知っている好奇心。 – DeusAduro

答えて

2

私は最高の解決策は、intのベクトルをとって、コンストラクタにテンプレートパラメータ 'rank'に対して検証させることです。

NDimensionalArray matrix(std::vector<int> matrixDimensions) 
{ 
    if (matrixDimensions.size() != rank) 
    { 
     throw SomeException(); 
    } 

    ... 
} 

ここでは、コンパイラのトリックを使用することはできません。 (マクロを使用するperhpsを除いて、何かを考えることができますが、それはコンパイラではありません)。

+0

私はこれについて心配していました。上記の私の編集を参照してください。私はクラスの「自然な」使用を可能にするすてきなクリーンなソリューションを見つけることを本当に望んでいます。アクセサーについて考えると、本当に問題になります。私は、コンストラクタ内のベクトルは一つのことですが、毎回ベクトルを使用する必要があれば、100万要素の配列にアクセスすることは非常に遅くなります。 – DeusAduro

+0

@DeusAduroサイズをあらかじめ指定して、プロセッサのキャッシュに優しい要素にアクセスすると遅くなることはありません。ヒープに割り当てられた配列が必要であり、スタックに配置されていないので、別の方法にはなりません。 –

+0

あなたは正しいです。その場合は、可変引数のものを使用してください。コンストラクタとアクセサ/ミューテータの両方でそれを検証します。ここでの問題は、関数の引数の数を「テンプレート化」できないことです。 –

1

直接答えはありませんが、ブリッツライブラリをチェックしてください。

1

std :: tr1 ::配列を取ることができます。 Hmm:

#include <array> 

template< class valType, int rank > 
class NDimensionalArray 
{ 
public: 
    NDimensionalArray(const std::tr1::array<rank>& dims); 
    // ... 
}; 

NDimensionalArray<double,2> foo({10,10}); 

NDimensionalArray<double,2> bar({10}); // second dimension would be 0 

NDimensionalArray<double,1> baz({10,10}); // compile error? 

実際にはうまくいきません。私はまるでそれを実行します。このアプローチは、より多くのようになりますように

編集コメントを1としては、なります

std::tr1::array<2> dims = {10, 10}; 
NDimensionalArray<double,2> foo(dims); 
+0

集計の初期化子は、変数を初期化するときにのみ使用でき、任意の式(関数やコンストラクタの引数など)では使用できないため、動作しません。 –

+0

tr1配列のヘッダーを見つけられませんでしたが、あなたはそれを言うと、過去に私を噛んでいることを覚えていると思います。 –

+0

は、この上で私を引用しないでください、私は初期化子リストは本格的なタイプ(のstd ::この場合はinitializer_list )に変更すると、これは有効なC++ 0xのコードになると信じて:http://en.wikipedia.org /ウィキ/ Cまた%2B%2B0x#Initializer_lists –

1

C++として、現在標準化でそれを行うには良い方法はありません。C++ 0xのでは、テンプレートパラメータを使用することができます(私は、構文の権利を持っていると思うが、requiresの拡大についてはよく分からない)近似するパック:

template <class ValType, int Rank> 
struct NDimensionalArray 
{ 
    template <class... Args> 
    requires std::SameType<Args, ValType>... && std::True<sizeof...(Args) == Rank> 
    NDimensionalArray(Args... values) 
    { 
     ... 
    } 
}; 
+0

のように聞こえますが、シンプルな例がなくても文法は私を少し超えていますが、それでも涼しいです。 – DeusAduro

0

ブーストはmulti-arrayライブラリを持っています多次元配列を構築するためのカスタムオブジェクトを使用します。それは本当に良い方法です。私はあなたのコードを勉強する(または、より良い、しかし、使用する)ことをお勧めします。

+0

面白いもの。もちろん、クラスを使うこともできますが、この種の実装は常に教育的です。私はva_list(...)で作業していますが、壊れやすいようですね?単語がちょうど非常に堅牢に見えないことは確かではありません。 – DeusAduro

+0

Cがタイプセーフではありませんすべてで、関数に渡される引数の種類や量を制限する方法はありませんよう可変引数使用するすべてのソリューション、およびいずれかの実行時に正しさのためにそれを検証する方法はありません。 –

7

さて、私はしばらくの間、これでプレイしました。ここでは、あなたが望むものに近い何かをするテンプレートのメタプログラミングのハッピーがあります。すべてのディメンションをインラインで指定できます。動的メモリ割り当てなどは行いません。何のコピーが行われていないとまた、優れたC++コンパイラ(私はVC++ /O2オプションでテスト)で、コードが完全に(実際には、私のためにそれがコールの時点で全体NDimensionalArrayコンストラクタをインライン化)、インライン化されます。それはコンパイル時に完全に型チェックされ、あまりにも少なすぎたり、大きすぎる次元を渡すことはできません。インデクサに再利用できます。ここに行く:好奇心のうち

template<class T, int N> 
class value_pack : private value_pack<T, N-1> 
{ 
public: 

    enum { size = N }; 

    value_pack(value_pack<T, N-1> head, const T& tail) 
     : value_pack<T, N-1>(head) 
     , value(tail) 
    { 
    } 

    value_pack<T, N+1> operator() (const T& tail) const 
    { 
     return value_pack<T, N+1>(*this, tail); 
    } 

    template<int I> 
    const T& get() const 
    { 
     return this->value_pack<T, I+1>::value; 
    } 

protected: 

    const T value; 
}; 

template<class T> 
struct value_pack<T, 0> 
{ 
}; 

struct 
{ 
    template <class T> 
    value_pack<T, 1> operator() (const T& tail) const 
    { 
     return value_pack<T, 1>(value_pack<T, 0>(), tail); 
    } 
} const values; 


template <class ValType, int Rank> 
struct NDimensionalArray 
{ 
    NDimensionalArray(value_pack<ValType, Rank> values) 
    { 
     // ... 
    } 
}; 


int main() 
{ 
    NDimensionalArray<int, 3> a(values(1)(2)(3)); 
} 
+0

+1非常にトリッキーなサー。再帰的なテンプレートプログラミングは、間違いなく動作する可能性がありますし、最速のソリューションである可能性もあります。 – DeusAduro

関連する問題