Создайте статический массив с шаблонами - PullRequest
23 голосов
/ 19 мая 2011

Был ответ на stackoverflow (который я, кажется, больше не могу найти), который продемонстрировал, как шаблон variadic может использоваться в 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_ с любым количеством параметров, которые затем будут скопированы во время компиляции во внутренний массив. Это полезный способ создания мета-функций для генерации константных массивов во время компиляции.

Однако одна проблема состоит в том, что от параметров шаблона класса зависит получение фактических значений для заполнения массива. Это приводит к одному главному ограничению: только интегральные константы могут использоваться в качестве параметров шаблона значения. Таким образом, вы не можете использовать эту технику для генерации массивов пользовательских типов.

Я пытался придумать что-нибудь, чтобы обойти это ограничение, но ничего не могу придумать. Есть ли способ заставить эту технику работать с нецелыми константами?

Ответы [ 3 ]

6 голосов
/ 20 июня 2011

Что ж, вы действительно можете заполнить статический массив экземплярами пользовательских типов (то есть классов) при условии, что они могут быть построены из целочисленных типов (или любых других типов, которые можно предоставить в качестве не шаблонных параметров и которые я не буду перечислять здесь).

Достаточно взглянуть на приведенный ниже пример, который, как мне кажется, достаточно ясен, чтобы объяснять его:

#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

2 голосов
/ 11 февраля 2014

В 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 и constexpr находятся в лучших местах.

1 голос
/ 19 мая 2011

Аргументы не типового шаблона также могут быть указателями или ссылками, если они указывают или ссылаются на объект с внешней связью.

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

Я не думаю, что вы действительно захотите инициировать элементы с внешними ссылками, так как это приведет к удвоению числа объектов. Вы можете инициализировать элементы массива из литералов, но, к сожалению, вы не можете использовать строковые литералы в качестве аргументов шаблона . Так что вам нужен пословичный уровень косвенности: Это больно, потому что массивы или ссылки на массивы не могут появляться в списке параметров шаблона (я думаю, именно поэтому строковые литералы не могут):

// 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 }, ... }), оставленных в качестве упражнения. Но вот пример .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...