使用可变参数模板创建静态数组

在stackoverflow上有一个答案(我似乎再也找不到了),它演示了如何在C ++ 11中使用可变模板在编译时创建一个静态数组:

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_ ,然后将它们在编译时复制到内部数组中。 在编译时创建用于生成常量数组的元函数是一种非常有用的方法。

但是,一个问题是它依赖于类模板参数来获取实际值来填充数组。 这导致一个主要限制:只有整数常量可以用作值模板参数。 所以,你不能用这种技术来生成自定义类型的数组。

我试图想办法解决这个限制,但不能提出任何问题。 有没有办法使这种技术与非整数常量一起工作?


那么,你确实可以用自定义类型(即类)实例填充静态数组,只要它们可以从整数类型(或者其他可以作为非模板参数提供的类型以及我不打算枚举的类型)构造。

只要看看下面的例子,我相信这是足够清楚的自我解释:

#include <iostream>

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

        void print_something()
        {
            std::cout << "somethingn";
        }
};

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下编译得很好


在C ++ 11中(特别是在C ++ 14中),在编译时初始化对象的最好方法是使用constexpr构造函数,而不是使用类型系统来使用metagames。

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 %dn", m.x_, m.y_);
    }

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

我不知道为什么Clang 3.5和GCC 4.8坚持我把HACK()宏放在那里,但是他们拒绝在没有它的情况下编译代码。 可能我犯了一些愚蠢的错误,有人可以指出。 另外,我并不确定所有的constconstexpr都处于最佳位置。


非类型模板参数也可以是指针或引用,只要它们指向或引用具有外部链接的对象。

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引用来初始化元素,因为这会导致对象数量的两倍。 您可以从文字初始化数组的元素,但不幸的是,您不能将字符串文字用作模板参数。 所以你需要一个间接的谚语层:这很痛苦,因为数组或数组引用不能出现在模板参数列表中(我猜这就是为什么字符串文字不能):

// 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情况下避免动态初始化,即使这样也有局限性。 使用某种元组来构建复合初始值设定项(例如,从{ { hello, world, 42 }, ... }初始化)作为练习。 但是这里有一个例子。

链接地址: http://www.djcxy.com/p/66885.html

上一篇: Create static array with variadic templates

下一篇: Is there an idiomatic way handle state in clojure?