Анонимный союз и структура
Как бы вы это сделали в стандартном С++ 11/14? Потому что, если я не ошибаюсь, это не стандартный совместимый код с анонимными структурами.
Я хочу получить доступ к элементам так же, как с этим.
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; };
struct { some_type r, g, b; };
some_type elements[3];
};
};
Ответы
Ответ 1
Да, ни С++ 11, ни С++ 14 не позволяют анонимные структуры. Этот ответ содержит некоторые аргументы в пользу того, почему это так. Вам нужно назвать структуры, и они также не могут быть определены в анонимном объединении.
§9.5/5 [class.union]
...
Спецификация участника анонимного объединения определяет только нестатические элементы данных. [Примечание. Вложенные типы, анонимные союзы и функции не могут быть объявлены в анонимном объединении. -end note]
Поэтому переместите определения структуры вне объединения.
template <typename some_type>
struct vec
{
struct xyz { some_type x, y, z; };
struct rgb { some_type r, g, b; };
union {
xyz a;
rgb b;
some_type elements[3];
};
};
Теперь мы требуем, чтобы some_type
был стандартным макетом, потому что все члены макета анонимного объединения совместимы. Вот требования для стандартного типа макета. Они описаны в разделе § 9/7 стандарта.
Затем из §9.2 [class.mem]
16 Два типа стандартной структуры (раздел 9) совместимы с макетами, если они имеют одинаковое количество нестатических элементов данных и соответствующих нестатических элементов данных (в порядке их описания) имеют совместимые с макетами типы (3.9).
18 Если объединение стандартного макета содержит две или более структуры стандартного макета, которые разделяют общую начальную последовательность, а если объект объединения стандартного макета в настоящее время содержит одну из этих структур стандартной компоновки, разрешается проверять общую начальную часть любого из них. Две структуры стандартного макета разделяют общую начальную последовательность, если соответствующие члены имеют совместимые с макетами типы, и ни один из них не является битовым полем, либо оба являются битовыми полями с одинаковой шириной для последовательности из одного или нескольких начальных элементов.
И для элемента массива, из §3.9/9 [basic.types]
...
Скалярные типы, типы класса стандартного макета (раздел 9), массивы таких типов и cv-квалифицированные версии этих типов (3.9.3) в совокупности называются стандартными типами макетов.
Чтобы гарантировать, что some_type
является стандартным макетом, добавьте следующее в определение vec
static_assert(std::is_standard_layout<some_type>::value, "not standard layout");
std::is_standard_layout
определяется в заголовке type_traits
. Теперь все 3 члена вашего союза являются стандартными макетами, две структуры и массив совместимы с макетами, и поэтому члены 3 профсоюза имеют общую начальную последовательность, которая позволяет вам писать, а затем проверять (читать) любые члены, принадлежащие к общему начальная последовательность (вся вещь в вашем случае).
Ответ 2
Анонимные союзы разрешены в С++ 11/14. См. Пример их использования в Bjarne Stroustrup С++ 11 FAQ
Относительно анонимных структур см. Почему С++ 11 не поддерживает анонимные структуры, а C11 -? и Почему С++ запретить анонимные структуры и союзы?
Хотя большинство компиляторов поддерживают анонимные структуры, если вы хотите, чтобы ваш код был стандартным, вы должны написать что-то вроде этого:
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; } s1;
struct { some_type r, g, b; } s2;
some_type elements[3];
};
};
Ответ 3
Я думаю, что другие ответы вроде упустили точку вопроса:
Я хочу получить доступ к элементам так же, как с этим.
Иными словами, вопрос в самом деле "как определить тип vec
стандартно-совместимым образом, так что заданный объект u
этого типа, u.x
, u.r
и u.elements[0]
все относятся к одной и той же вещи?"
Хорошо, если вы настаиваете на этом синтаксисе... тогда очевидный ответ: ссылки.
Итак:
template <typename some_type>
struct vec
{
vec() = default;
vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {}
vec & operator=(const vec &other) {
elements[0] = other.elements[0];
elements[1] = other.elements[1];
elements[2] = other.elements[2];
return *this;
}
some_type elements[3];
some_type &x = elements[0], &y = elements[1], &z = elements[2];
some_type &r = elements[0], &g = elements[1], &b = elements[2];
};
Первая проблема с этим подходом заключается в том, что вам нужно дополнительное пространство для 6 ссылочных элементов - что довольно дорого для такой небольшой структуры.
Вторая проблема с этим подходом заключается в том, что данный const vec<double> v;
, v.x
по-прежнему имеет тип double &
, поэтому вы можете написать v.x = 20;
и скомпилировать его без предупреждения или ошибки - только для получения поведения undefined, Довольно плохо.
Итак, в качестве альтернативы вы можете использовать функции доступа:
template <typename some_type>
struct vec
{
some_type elements[3];
some_type &x() { return elements[0]; }
const some_type &x() const { return elements[0]; }
some_type &y() { return elements[1]; }
const some_type &y() const { return elements[1]; }
some_type &z() { return elements[2]; }
const some_type &z() const { return elements[2]; }
some_type &r() { return elements[0]; }
const some_type &r() const { return elements[0]; }
some_type &g() { return elements[1]; }
const some_type &g() const { return elements[1]; }
some_type &b() { return elements[2]; }
const some_type &b() const { return elements[2]; }
};
Вам нужно написать u.x()
и т.д. вместо u.x
, но экономия пространства значительна, вы также можете полагаться на специальные функции-члены, сгенерированные компилятором, тривиально скопируемые, если some_type
(что позволяет некоторые оптимизации), это совокупность и поэтому может использовать синтаксис агрегатной инициализации, а также const-correct.
Демо. Обратите внимание, что sizeof(vec<double>)
составляет 72 для первой версии и только 24 для второго.