2

もっと簡単な方法があることは知っていますが、コンパイル時に、2次元配列の展開されたインデックスからその一般的な形式へのマップを初期化したいと思います。

配列オブジェクトをインスタンス化する必要なく、これを行いたいと思います。

以下では、 からマップを定義しますarray[][]->array[][] -> [][] ここで、選択したマッピング スキームをハードコーディングせずに、反対のことを行う方法を 知りたいと思います。

メタプログラミングと可変個引数テンプレートを使用すると、それが可能になるはずです。でも、数日前に初めて使ってみたので、慣れるまで時間がかかります;)

ヘッダ:

template <int dim>
class internal {
    static unsigned int table[dim][dim];
    static unsigned int x_comp[dim*dim];
    static unsigned int y_comp[dim*dim];
};

ソース:

//1d case:

template <>
unsigned int
internal<1>::table[1][1]  = {{0}};

template <>
unsigned int
internal<1>::x_component[1] = {0};

template <>
unsigned int
internal<1>::y_component[1] = {0};

//2d case:

template<>
unsigned int
internal<2>::table[2][2] =
            {{0, 1},
             {2, 3}
            };

// here goes some metaprogramming tricks to initialize
// internal<2>::y_component[2*2] = ...
// internal<2>::x_component[2*2] = ... 
// based on mapping above, i.e. table[2][2];
// that is:
// x_table = { 0, 0, 1, 1 }
// y_table = { 0, 1, 0, 1 }
// 
// so that :
//
//  index == table[i][j]
//  i     == x_comp[index]
//  j     == y_comp[index]

EDIT1:

または、それは不可能であり、すべてをハードコーディングするか、整数除算を使用して 2 つのインデックス表現を関連付けると言ってください。

EDIT2:任意の配列の定義に固執したいと思います。もちろん、整数除算を使用した以下の回答のように、なくてもかまいません。

これらの配列は、たとえば次のように任意に設定できます。

template<>
unsigned int
internal<2>::table[2][2] =
            {{3, 0},
             {2, 1}
            };
4

3 に答える 3

3

配列の使用:

0 から dim^2-1 までの一意のエントリを持つテーブルが与えられた場合constexpr、特定のテーブル エントリの i と j のルックアップ関数を記述できます。

constexpr unsigned get_x_comp(unsigned index, unsigned i=0, unsigned j=0) 
{ return table[i][j] == index ? i : get_x_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); }

constexpr unsigned get_y_comp(unsigned index, unsigned i=0, unsigned j=0) 
{ return table[i][j] == index ? j : get_y_comp(index, ((j+1)%dim ? i : i+1), (j+1)%dim); }

これらは再帰的に自分自身を呼び出し、テーブルを反復処理して を探しindexます。i指定されたインデックスが見つかり、そのインデックスの/jが返されると、再帰は最終的に終了します。

それをJonathanが言及したC++ 14と組み合わせてstd::integer_sequence、配列を初期化します。

template<unsigned... I>
constexpr auto make_x_comp(std::integer_sequence<unsigned, I...>) -> std::array<unsigned, sizeof...(I)> { return {get_x_comp(I)...}; }

配列の代わりにメタ関数を使用する:

状況によっては、配列さえ必要ない場合もあります。table0 から dim^2-1 までの連続したインデックスを含める必要があると仮定します。その場合、tablex_compおよびy_compは、次の属性を持つ単純なコンパイル時関数のみです。

  • table(i,j) := i*dim + j
  • x_comp(index) := index / dim(整数除算)
  • y_comp(index) := index % dim

C++11 機能を利用できるかどうかによって、実装は異なりますが、どちらも配列なしです。

注:table次の実装では、格納されている数値が 0 から dim^2-1 まで連続していると想定します。そうでない場合は、独自の適切な関数をロールバックしtableて、上記get_x_compget_y_comp実装を使用する必要があります

C++11:

template <unsigned dim> //use unsigned to avoid negative numbers!
struct internal {
  static constexpr unsigned table(unsigned i, unsigned j) { return i*dim+j; }
  static constexpr unsigned x_comp(unsigned index) { return index/dim; }
  static constexpr unsigned y_comp(unsigned index) { return index%dim; }
};

これらの関数は、通常の関数と同じようにどこでも呼び出すことができます。特に、コンパイル時の定数が必要な場合はどこでも呼び出すことができます。例:int a[internal<5>::table(2,4)];

C++03:

template <unsigned dim> //use unsigned to avoid negative numbers!
struct internal {
  template<unsigned i, unsigned j>
  struct table{ static const unsigned value = i*dim+j; };
  template<unsigned index>
  struct x_comp{ static const unsigned value = index/dim; };
  template<unsigned index>
  struct y_comp{ static const unsigned value = index%dim; };
};

これらのメタ関数の使用は、C++11 よりも少し扱いに​​くいですが、テンプレート メタ関数では通常どおりに機能します。上記と同じ例:int a[internal<5>::table<2,4>::value];

注:今回は、(メタ) 関数をヘッダーに配置できます。これは、それらが非整数の静的メンバー変数でなくなったためです。また、テンプレートを小さい寸法に制限する必要はありません。これは、すべてが より小さい寸法で適切に計算されるためですsqrt(numeric_limits<unsigned>::max())

于 2013-06-18T12:51:39.420 に答える
1

質問に直接答えていない (またはまったく答えていない) 場合は申し訳ありませんが、あなたが何を求めているのかよくわかりません。あなたが言っているのは、コンパイル時に、サイズN x Mの配列を1D 配列として表す方法を初期化したいということですか?

正方形以外の寸法を割り当てることができるコードを含めました。私はこれを「簡単な」C++ で作成したので、テンプレートを使用するだけであれば、従うのはそれほど難しくありません。

このようなことは可能ですか?

template <typename T, typename std::size_t N, typename std::size_t M = 1>
class Array {
    T* data;
public:
    Array<T, N, M>() : data(new T[N * M]) {
        T temp = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                data[i * M + j] = temp++;
            }
        }
    }
    /* methods and stuff
}

M は列番号なので、次のように使用します。

int main(void) {

    Array<float, 10, 10> myArray;

    return 0;
}

deleteデストラクタを呼び出すことを忘れないでください。

于 2013-06-18T12:47:49.010 に答える
1

編集:入力のルールを理解していませんでした。これx_compy_comp書いたとき、質問の一部がわかったので、この回答は実際には関係ありませんtable。連続する整数のみが含まれていると誤って想定していたためです。Arneの(はるかに優れた)回答がそれを参照しているため、回答はとにかくここに残されています。


std::array配列をC++14integer_sequenceユーティリティに置き換えて使用します。

template <int dim>
struct internal {
    static std::array<std::array<unsigned, dim>, dim> table;
    static std::array<unsigned, dim*dim> x_comp;
    static std::array<unsigned, dim*dim> y_comp;
};

template<unsigned Origin, unsigned... I>
constexpr std::array<unsigned, sizeof...(I)>
make_1d_array_impl(std::integer_sequence<unsigned, I...>)
{
    return { { I + Origin ... } };
}

template<int N>
constexpr std::array<unsigned, N*N>
make_1d_array()
{
    return make_1d_array_impl<0>(std::make_integer_sequence<unsigned, N*N>{});
}


template<unsigned... I>
constexpr std::array<std::array<unsigned, sizeof...(I)>, sizeof...(I)>
make_2d_array_impl(std::integer_sequence<unsigned, I...> seq)
{
    return { { make_1d_array_impl<I*sizeof...(I)>(seq)  ... } };
}

template<int N>
constexpr std::array<std::array<unsigned, N>, N>
make_2d_array()
{
    return make_2d_array_impl(std::make_integer_sequence<unsigned, N>{});
}

template<int dim>
std::array<std::array<unsigned, dim>, dim> internal<dim>::table = make_2d_array<dim>();

tableこれにより、配列が正しく埋められます。必要に応じて入力するには、もう少し考えなければx_compなりy_compませんが、実行可能です。

https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.hinteger_sequenceでC++11 実装を見つけることができます。

于 2013-06-18T12:46:51.613 に答える