Можно определить статический массив во время компиляции следующим образом:
const std::size_t size = 5;
unsigned int list[size] = { 1, 2, 3, 4, 5 };
Вопрос 1 - действительно ли возможно при помощи различных видов методов метапрограммирования присвоить эти значения "программно" во время компиляции?
Вопросом 2 - Принимающий все значения в массиве состоит в том, чтобы быть тот же барристер некоторые, действительно ли возможно выборочно присвоить значения во время компиляции программным способом?
например:
const std::size_t size = 7;
unsigned int list[size] = { 0, 0, 2, 3, 0, 0, 0 };
ОБНОВЛЕНИЕ: решение Georg Fritzsche удивительно, нуждается в небольшой работе для получения его компилирующий на msvc и компиляторах Intel, но тем не менее очень интересном подходе к проблеме.
Самое близкое, что вы можете получить, - это использование функций C ++ 0x для инициализации локальных или членских массивов шаблонов из списка аргументов вариативного шаблона.
Это, конечно, ограничено максимальной глубиной создания экземпляра шаблона, и нужно будет измерить, что действительно имеет заметное значение в вашем случае.
Пример:
template<unsigned... args> struct ArrayHolder {
static const unsigned data[sizeof...(args)];
};
template<unsigned... args>
const unsigned ArrayHolder<args...>::data[sizeof...(args)] = { args... };
template<size_t N, template<size_t> class F, unsigned... args>
struct generate_array_impl {
typedef typename generate_array_impl<N-1, F, F<N>::value, args...>::result result;
};
template<template<size_t> class F, unsigned... args>
struct generate_array_impl<0, F, args...> {
typedef ArrayHolder<F<0>::value, args...> result;
};
template<size_t N, template<size_t> class F>
struct generate_array {
typedef typename generate_array_impl<N-1, F>::result result;
};
Использование для вашего случая 1..5
:
template<size_t index> struct MetaFunc {
enum { value = index + 1 };
};
void test() {
const size_t count = 5;
typedef generate_array<count, MetaFunc>::result A;
for (size_t i=0; i<count; ++i)
std::cout << A::data[i] << "\n";
}
Есть много вещей, которые можно сделать с помощью мета-программирования. Но сначала я хотел бы спросить: зачем вам это нужно в вашем случае? Я мог бы понять, если бы вам нужно было объявить такой массив в разных местах, так что это потребовало бы переписывать одни и те же вещи несколько раз. Это ваш случай?
Говоря "определить программно", я предполагаю следующее:
#define MyArr(macro, sep) \
macro(0) sep \
macro(0) sep \
macro(2) sep \
macro(3) sep \
macro(0) sep \
macro(0) sep \
macro(0)
К этому моменту мы определили все нужные вам значения наиболее абстрактным способом. BTW, если эти значения действительно что-то значат для вас - вы можете добавить это в декларацию:
#define MyArr(macro, sep) \
macro(0, Something1) sep \
macro(0, Something2) sep \
// ...
Теперь давайте вдохнем жизнь в приведенную выше декларацию.
#define NOP
#define COMMA ,
#define Macro_Count(num, descr) 1
#define Macro_Value(num, descr) num
const std::size_t size = MyArr(Macro_Count, +);
unsigned int list[size] = { MyArr(Macro_Value, COMMA) };
Вы также можете справиться с ситуацией, когда большинство записей вашего массива одинаковы, с помощью извращенного творчества :)
Но вы всегда должны спрашивать себя: действительно ли это того стоит? Ведь, как вы видите, вы превращаете код в головоломку.
Ну, ваши требования настолько расплывчаты, что трудно что-либо с ними сделать... Основной вопрос, конечно, в том, откуда берутся эти значения?
В любом случае, сборку на C++ можно представить как 4 этапа:
Если вы хотите исключить скриптовую генерацию, то у вас остается 2 альтернативы: Препроцессинг и программирование мета-шаблонов.
Я не знаю никакого способа, чтобы мета-шаблонное программирование помогло здесь, потому что, насколько я знаю, невозможно скомпилировать два массива во время компиляции. Таким образом, мы остаемся со спасителем дня: Препроцессорное программирование
Я бы предложил использовать полноценную библиотеку, чтобы помочь нам: Boost.Preprocessor.
Особый интерес здесь представляют:
Теперь, если бы мы только знали, откуда брать значения, мы могли бы привести более содержательные примеры.
Просто используйте генератор кода. Создайте один или несколько шаблонов, которые могут генерировать нужный вам код, используя таблицу или даже математические функции. Затем включите сгенерированный файл в свое приложение.
Серьезно, генератор кода значительно облегчит вам жизнь.
Действительно ли вам нужно делать это на этапе компиляции? Было бы гораздо проще сделать это во время статической инициализации. Можно сделать что-то вроде этого.
#include <cstddef>
#include <algorithm>
template<std::size_t n>
struct Sequence
{
int list[n];
Sequence()
{
for (std::size_t m = 0; m != n; ++m)
{
list[m] = m + 1;
}
}
};
const Sequence<5> seq1;
struct MostlyZero
{
int list[5];
MostlyZero()
{
std::fill_n(list, 5, 0); // Not actually necessary if our only
// are static as static objects are
// always zero-initialized before any
// other initialization
list[2] = 2;
list[3] = 3;
}
};
const MostlyZero mz1;
#include <iostream>
#include <ostream>
int main()
{
for (std::size_t n = 0; n != 5; ++n)
{
std::cout << seq1.list[n] << ", " << mz1.list[n] << '\n';
}
}
При желании вы можете вынести списки за пределы структур, но мне показалось, что так будет немного чище.
Что-то вроде Boost.Assignment может работать для стандартных контейнеров. Если вам действительно нужно использовать массивы, вы можете использовать его вместе с Boost.Array .
Как насчет создания вложенной структуры с использованием шаблонов и преобразования ее в массив правильного типа. Приведенный ниже пример работает для меня, но у меня такое чувство, что я либо наступаю, либо очень близок к неопределенному поведению.
#include <iostream>
template<int N>
struct NestedStruct
{
NestedStruct<N-1> contained;
int i;
NestedStruct<N>() : i(N) {}
};
template<>
struct NestedStruct<0>
{
int i;
NestedStruct<0>() : i(0) {}
};
int main()
{
NestedStruct<10> f;
int *array = reinterpret_cast<int*>(&f);
for(unsigned int i=0;i<10;++i)
{
std::cout<<array[i]<<std::endl;
}
}
И, конечно, вы можете возразить, что массив не инициализируется во время компиляции (что, я думаю, невозможно), но значения, которые войдут в массив, вычисляются во время компиляции, и вы можете получить к ним доступ, как и при обычном массив ... Я думаю, это как можно ближе.
the 1't question. Вы можете сделать это так.
template <int num, int cur>
struct ConsequentListInternal {
enum {value = cur};
ConsequentListInternal<num-1,cur+1> next_elem;
};
template <int cur>
struct ConsequentListInternal<0, cur> {
enum {value = cur};
};
template <int v>
struct ConsequentList {
ConsequentListInternal<v, 0> list;
};
int main() {
ConsequentList<15> list;
return 0;
}
из boost,
boost::mpl::range_c<int,1,5>
Будет генерировать список отсортированных чисел от 1 до 5 во время компиляции. Во втором случае вы не указали критериев, по которым будут изменяться значения. Я уверен, что вы не можете отменить определение, а затем переопределить новый var после создания списка.