#c #gcc #struct #forward-declaration #unions
#c #gcc #структура #прямое объявление #объединения
Вопрос:
В C я пытаюсь создать специализированный класс point в виде объединения, вот так:
union point
{
struct { float x, y, z; };
float val[3];
float operator[](unsigned i) { return val[i]; }
};
Чтобы я мог получить доступ к точке как к массиву или как к нескольким точкам, для удобства чтения.
Однако, давайте предположим, что я хочу немного обобщить это:
template<unsigned n>
union point
{
struct { float ???; };
float val[n];
float operator[](unsigned i) { return val[i]; }
};
Что я могу указать для ???
? Я мог бы иметь x
, x, y
x, y, z
или x, y, z, w
в зависимости от того, что n
есть. Решение? Пересылать объявления!
template<unsigned n>
union point
{
struct coords;
float val[n];
float operator[](unsigned i) { return val[i]; }
};
template<>
struct point::coords<3>
{
float x, y, z;
};
// ...
Но, похоже, это не работает. Однако в GCC 4.6 он компилируется всякий раз, когда я пытаюсь использовать элементы, например, так:
point<3> val;
val.x;
Я получаю сообщение об ошибке:
error: ‘union point<3>’ has no member named ‘x’
Даже если я изменю val.x
на val.coords::x
, я все равно получу ошибку:
error: ‘union point<3>::coords’ is not a base of ‘union point<3>’
Добавление using coords;
в определение объединения также не помогло.
Есть ли какой-либо способ выполнить это в GCC 4.6? Существует ли другой способ сделать это? Возможно ли это вообще?
Комментарии:
1. Если вам нужно специализировать каждый экземпляр шаблона, почему бы просто не объявить 4 разных объединения (без шаблонов) и покончить с этим?
2. Вас интересуют решения для «[доступа] к точке в виде массива или в виде нескольких точек, для удобства чтения» в более общем смысле тоже, или материал объединения необходим?
3. Luc — Основная причина в том, что в 99% случаев x, y, z и w будут использоваться в качестве средств доступа к элементам, но я все еще хочу разрешить возможность простого перебора значений точек без необходимости выполнять хакерское приведение типов указателей.
Ответ №1:
Я бы предложил использовать переменный макрос для определения ваших union<N>
шаблонов.
template<unsigned int N>
union point; // declared and undefined
#define DECLARE_POINT(NUM, ...)
template<>
union point<NUM>
{
struct { float __VA_ARGS__; };
float val[NUM];
}
#undef DECLARE_POINT
Сделав это, вы можете просто объявить / определить свои различные комбинации для координат (в данном случае перед #undef
):
DECLARE_POINT(1, x);
DECLARE_POINT(2, x, y);
DECLARE_POINT(3, x, y, z);
это эквивалентно,
template<> union point<1> { struct { float x; }; float val[1]; };
template<> union point<2> { struct { float x, y; }; float val[2]; };
template<> union point<3> { struct { float x, y, z; }; float val[3]; };
Его можно использовать так же, как вы просили:
point<3> p;
p.z = 0;
Кроме того, вы можете выполнить перекрестную проверку, используя некоторый шаблонный трюк ( static_assert
), чтобы проверить, что числовые аргументы (например, 1,2,3,...
) соответствуют общему переданному аргументу (например, x,y,z,...
).
Ответ №2:
Эта строка внутри вашего объединения:
struct coords;
forward — объявляет тип coords
, но в вашем шаблонном объединении нет struct coords
поля.
Кроме того, только члены анонимных структур могут быть доступны в качестве полей верхнего уровня объединения. Например:
union foo {
struct { // anonymous struct
short i;
short j;
};
int k;
};
foo f;
// it's possible to access the fields of the anonymous struct as if they were
// direct members of the union
f.i = 4;
f.j = 8;
std::cout << f.k;
Я не уверен, что вы сможете это сделать, если будете специализировать только внутренний struct
тип.
Однако это работает:
template<unsigned n>
union point;
template<>
union point<2> {
struct { float x, y; };
float val[2];
};
template<>
union point<3> {
struct { float x, y, z; };
float val[3];
};
Однако есть ряд недостатков; главный из них заключается в том, что вам придется переопределять operator[]
для каждой версии point
.
Я знаю, как использовать шаблоны, но я не бог шаблонов, поэтому не исключено, что существует хитрый трюк.
Комментарии:
1. Следует упомянуть, что анонимные структуры не являются частью C , а всего лишь популярным расширением (и станут частью C1x, следующей версии C; однако, не C 0x).