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

C++ 使用可变模板创建静态数组,c++,templates,c++11,variadic-templates,C++,Templates,C++11,Variadic Templates,关于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...(ar

关于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... };
模板
结构数组_
{
静态常数数据[sizeof…(args)];
};
模板
常量数组_u2;::数据[sizeof…(args)]={args…};
可以提供递归元函数来实例化带有任意数量参数的
数组
,然后在编译时将这些参数复制到内部数组中。这是一种在编译时创建元函数以生成常量数组的有用方法

然而,一个问题是,它依赖于类模板参数来获得填充数组的实际值。这导致了一个主要限制:只能将整型常量用作值模板参数。因此,不能使用此技术生成自定义类型的数组


我试着想办法绕过这个限制,但什么都想不出来。有什么方法可以使这种技术与非整型常量一起工作吗?

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

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
模板
结构参考{
静态T&
get(){return t;}
};
int i=0;
int&ri=ref::get();//好啊
静态int j=0;
int&rj=ref::get();//不好
常数int jj=0;//在这里,const意味着内部链接;哎呀
const int&rjj=ref::get();//不好
外部常数int k=0;
常量int&rk=ref::get();//好啊
名称空间{
int l=0;
}
int&rl=ref::get();//好的,l是特定于TU的
我认为您不会真的想使用外部引用初始化元素,因为这样会导致对象数量增加一倍。您可以从文本初始化数组的元素,但不幸的是,您不能。因此,您需要众所周知的间接层:这很痛苦,因为数组或数组引用不能出现在模板参数列表中(我想这就是字符串文本不能出现的原因):

//不可能:
//const char*lits[]={“你好”,“世界!”;
//lit接受常量字符*&,而不是常量字符*
//typedef数组uu数组;
//相反,但痛苦的是:
const char*hello=“hello”;
const char*world=“world!”;
typedef数组uu数组;
/*
*这里的数组::数据是一个T的数组,大小为3,
*从{你好,世界,42}初始化
*/

如果没有C++0x的
constexpr
,我看不出如何避免动态初始化,甚至还有一些限制。使用某种元组构建复合初始值设定项(例如,从
{{{hello,world,42},…}
初始化)作为练习。但是。

好吧,您确实可以用自定义类型(即类)实例填充静态数组,只要它们可以从整数类型(或任何其他类型,可以作为非模板参数提供,我在这里不列举)构造

请看下面的例子,我相信它足够清楚,可以自我解释:

#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();
}
#包括
模板
上我的课
{
公众:
我的班级(T)
{
//构造
}
作废打印某物
{
std::cout在C++11(尤其是C++14)中,在编译时初始化对象的最佳方法是使用
constepr
构造函数,而不是通过与类型系统玩元游戏

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
}
#包括
#如果uu cplusplus<201400
模板结构整型_序列{typedef integer_序列类型;};
模板结构生成索引序列;
模板结构make_index_sequence:integer_sequence{};
模板结构make_index_序列:make_index_序列{};
#定义HACK(x)typename x::type
#否则
#包括//C++14的做事方式
使用std::integer_序列;
使用std::make_index_序列;
#定义HACK(x)x
#恩迪夫
结构对象{
int x_uux,y_uux;
constexpr MyObject(intx,inty):x_x(x),y_y(y){
};
模板
结构生成ArrayHelper;
模板
结构生成ArrayHelper{
静态常量数据[N];//元素i用Func(i)初始化
};
模板
const T GeneratedArrayHelper::数据[N]=
{Func(i)…};
模板
结构生成阵列:
生成ArrayHelper{};
constexpr MyObject newObject(inti){返回MyObject(2*i,2*i+1);}
int main(){
for(常量MyObject&m:GenerateArray::data){
printf(“%d%d\n”,m.x\u,m.y\u);
}
//输出:
//   0 1
//   2 3
//   4 5
//   6 7
//   8 9
}

我不知道为什么Clang3.5和GCC4.8坚持我把
HACK()放在
宏,但他们拒绝编译没有它的代码。可能我犯了一些愚蠢的错误,有人可以指出。此外,我不确定所有的
常量和
常量是否都在最佳位置。

std::string可能在内部有一个动态内存分配。在compile time?:-)@Bo,是的,std::string是一个坏例子。但是自定义POD结构呢?我相信这就是问题中提到的。看看我的问题,有一个很好的答案:@BoPersson不可能创建编译时字符串,但可以创建编译时c-string并在运行时将其转换为字符串(或者,如果使用c++17,也可以在编译时字符串_视图)。您可以找到此技术的示例用法以及一些相关的元函数。
std::string
无法构造
constexpr
,因为它使用动态分配。@Ben实际上,因为我使用的是文本,所以从文本的组合初始化时会出现问题(请不要断章取义地引用我的话).如果元素类型
T
不能静态初始化,那么显然这是不可能发生的,但这与问题和我的答案无关。
template
参数
#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
}