There was an answer on stackoverflow (which I can\'t seem to find anymore) which demonstrated how a variadic template can be used in C++11 to create a static array at compil
Non-type template arguments can also be pointers or references, provided they point or refer to an object with external linkage.
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
I don't think you'd really want to init the elements with extern references though, since that would end up with twice the number of objects. You could initialize the elements of the array from literals, but unfortunately you can't use string literals as template arguments. So you'd need the proverbial layer of indirection: It's painful because arrays or array references can't appear in a template parameter list (I guess this is why string literals can't):
// 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 }
*/
I can't see how to avoid dynamic initialization without C++0x's constexpr
, and even then there are limitations. Using some kind of tuple to build composite initializers (e.g. initialize from { { hello, world, 42 }, ... }
) left as an exercise. But here's an example.
Well, you can indeed populate a static array with custom types (i.e. classes) instances provided that they are constructable from integer types (or whatever other types one may provide as non template parameters and which I am not going enumerate here).
Just take look at the example bellow, which I believe is clear enough to be self explaining:
#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();
}
Note: compiled just fine under GCC 4.6
In C++11 (and especially in C++14), the best way to initialize objects at compile-time is with constexpr
constructors, not by playing metagames with the typesystem.
struct MyObject {
int x_, y_;
constexpr MyObject(int x, int y) : x_(x), y_(y) { }
};
const MyObject array[] = { MyObject(1,2), MyObject(3,4) };
You can apply your "generator function" idea here, too, if you really want to:
#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
}
I don't know why Clang 3.5 and GCC 4.8 insist that I put the HACK()
macro in there, but they refuse to compile the code without it. Probably I made some dumb mistake and someone can point it out. Also, I'm not confident that all the const
s and constexpr
s are in the best places.