Анонимный союз и структура

Как бы вы это сделали в стандартном С++ 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 для второго.