26

C++ 11 で可変引数テンプレートを使用してコンパイル時に静的配列を作成する方法を示す、stackoverflow に関する回答 (これはもう見つけられないようです) がありました。

template <class T, T... args> 
struct array_
{
    static const T data[sizeof...(args)];
};

template <class T, T... args> 
const T array_<T, args...>::data[sizeof...(args)] = { args... };

再帰的なメタ関数を提供してarray_、任意の数のパラメーターでインスタンス化することができます。これは、コンパイル時に内部配列にコピーされます。これは、コンパイル時に定数配列を生成するためのメタ関数を作成する便利な方法です。

ただし、1 つの問題は、配列に入力する実際の値を取得するために、クラス テンプレート パラメーターに依存することです。これにより、1 つの大きな制限が生じます。値テンプレート パラメーターとして使用できるのは、整数定数のみです。したがって、この手法を使用してカスタム型の配列を生成することはできません。

この制限を回避する方法を考えようとしましたが、何も思いつきません。この手法を非整数定数で機能させる方法はありますか?

4

3 に答える 3

6

実際、整数型 (または非テンプレート パラメーターとして提供され、ここでは列挙しない他の型) から構築可能であれば、静的配列にカスタム型 (つまり、クラス) のインスタンスを設定できます。

以下の例を見てください。これは、自己説明するのに十分明確であると私は信じています。

#include <iostream>

template<typename T>
class my_class
{
    public:
        my_class(T)
        {
            //construct
        }

        void print_something()
        {
            std::cout << "something\n";
        }
};

template<class C, class T, T ... args>
struct array_
{
        static C data[sizeof...(args)];
};

template<class C, class T, T ... args>
C array_<C, T, args...>::data[sizeof...(args)] = {C(args)...};

int main()
{
    array_<my_class<int> , int, 1, 200, 0, 42>::data[2].print_something();
}

注:GCC 4.6で問題なくコンパイルされました

于 2011-06-20T15:13:10.000 に答える
2

C++11 (特に C++14) では、コンパイル時にオブジェクトを初期化する最善の方法はconstexpr、型システムでメタゲームをプレイするのではなく、コンストラクターを使用することです。

struct MyObject {
    int x_, y_;
    constexpr MyObject(int x, int y) : x_(x), y_(y) { }
};

const MyObject array[] = { MyObject(1,2), MyObject(3,4) };

本当にしたい場合は、ここでも「ジェネレーター関数」のアイデアを適用できます。

#include <stdio.h>

#if __cplusplus < 201400
template<size_t... II> struct integer_sequence { typedef integer_sequence type; };
template<size_t N, size_t... II> struct make_index_sequence;
template<size_t... II> struct make_index_sequence<0, II...> : integer_sequence<II...> {};
template<size_t N, size_t... II> struct make_index_sequence : make_index_sequence<N-1, N-1, II...> {};
#define HACK(x) typename x::type
#else
#include <utility>  // the C++14 way of doing things
using std::integer_sequence;
using std::make_index_sequence;
#define HACK(x) x
#endif


struct MyObject {
    int x_, y_;
    constexpr MyObject(int x, int y) : x_(x), y_(y) { }
};

template<typename T, int N, T (*Func)(int), typename Indices>
struct GeneratedArrayHelper;

template<typename T, int N, T (*Func)(int), size_t... i>
struct GeneratedArrayHelper<T, N, Func, integer_sequence<i...>> {
    static const T data[N];  // element i is initialized with Func(i)
};

template<typename T, int N, T (*Func)(int), size_t... i>
const T GeneratedArrayHelper<T,N,Func, integer_sequence<i...>>::data[N] =
    { Func(i)... };

template<typename T, int N, T (*Func)(int)>
struct GeneratedArray :
    GeneratedArrayHelper<T, N, Func, HACK(make_index_sequence<N>)> {};

constexpr MyObject newObject(int i) { return MyObject(2*i, 2*i+1); }

int main() {
    for (const MyObject& m : GeneratedArray<MyObject, 5, newObject>::data) {
        printf("%d %d\n", m.x_, m.y_);
    }

    // Output:
    //   0 1
    //   2 3
    //   4 5
    //   6 7
    //   8 9
}

なぜ Clang 3.5 と GCC 4.8 がマクロをそこに入れるように主張するのかはわかりませんHACK()が、それなしでコードをコンパイルすることを拒否しています。おそらく私は愚かな間違いを犯し、誰かがそれを指摘することができます. constまた、すべてのsとconstexprsが最良の場所にあるという自信はありません。

于 2014-02-10T23:27:27.153 に答える
1

非型テンプレート引数は、外部リンケージを持つオブジェクトを指すか参照する場合、ポインターまたは参照にすることもできます。

template<typename T, T& t>
struct ref {
    static T&
    get() { return t; }
};

int i = 0;
int& ri = ref<int, i>::get(); // ok

static int j = 0;
int& rj = ref<int, j>::get(); // not ok

const int jj = 0; // here, const implies internal linkage; whoops
const int& rjj = ref<const int, jj>::get(); // not ok

extern const int k = 0;
const int& rk = ref<const int, k>::get(); // ok

namespace {
int l = 0;
}
int& rl = ref<int, l>::get(); // ok, and l is specific to the TU

ただし、extern 参照を使用して要素を初期化する必要はないと思います。オブジェクトの数が 2 倍になるためです。配列の要素をリテラルから初期化することはできますが、残念ながら文字列リテラルをテンプレート引数として使用することはできません。したがって、間接的なことわざのレイヤーが必要になります。配列または配列参照がテンプレートパラメーターリストに表示されないため、苦痛です (これが、文字列リテラルが表示されない理由だと思います)。

// Not possible:
// const char* lits[] = { "Hello, ", "World!" };
// lit accepts const char*&, not const char*
// typedef array_<T, lit<lits[0]>, lit<lits[1]>, int_<42> > array;

// instead, but painful:
const char* hello = "Hello";
const char* world = "World!";
typedef array_<T, lit<hello>, lit<world>, int_<42> > array;
/*
 * here array::data would be an array of T, size 3,
 * initialized from { hello, world, 42 }
 */

C++0x を使用せずに動的初期化を回避する方法がわかりませんconstexpr。それでも制限があります。ある種のタプルを使用して複合初期化子を構築する (例: initialize from { { hello, world, 42 }, ... }) は演習として残しました。しかし、ここに例があります。

于 2011-05-19T16:11:37.257 に答える