Как помещать анонимный союз с 0

Как отключить анонимный союз? Я не мог найти что-либо на странице cppreference об этом. Может ли memset задействовать его наибольший член с 0?

Например -

#include <iostream>
#include <cstring>

struct s{
    char a;
    char b[100];
};

int main(){
 union {
   int a;
   s b;
   char c;
 };

  // b.a = 'a'; (1)

  std::memset(&b, 0, sizeof(b));

  std::cout << a << "\n";
  std::cout << b.a << " " << b.b << "\n";
  std::cout << c << "\n";
}

Также, если это сработает, следует ли раскомментировать (1) перед тем, как использовать memset() для активации самого большого члена?

Ответ 1

Если вы действительно хотите уважать стандарт, вы должны знать, что код, который вы написали, - это undefined поведение: С++ standard §3.8 [basic.life]:

... за исключением того, что если объект является членом объединения или его подобъектом, его время жизни начинается только в том случае, если этот член объединения является инициализированным членом в союзе (8.6.1, 12.6.2) или как описано в 9.3. Время жизни объекта o тип T заканчивается, когда: (1.3) - если T - тип класса с нетривиальным деструктором (12.4), начинается вызов деструктора или (1.4) - хранилище, которое занимает объект, освобождается или повторно используется объектом, который не вложен в o (1.8).

В §9.3 объясняется, что вы можете активировать член стандартного союза макета, назначив ему. Это также объясняет, что вы можете изучить значение члена объединения, который не активируется только при соблюдении определенных критериев:

Если соединение стандартного макета содержит несколько структур стандартной компоновки, которые имеют общую начальную последовательность (9.2), и если нестатические данные член объекта этого типа стандартного типа макета является активным и является одной из структур стандартной компоновки, разрешено проверять общую начальную последовательность любого из элементов структуры стандартного макета; см. 9.2. - конечная нота]

Итак, когда вы пишете std::cout<< a << "\n", вы не инициализировали a или активировали его назначением, и ни один член не был инициализирован, поэтому вы находитесь в Undefined Поведение (Nota: но компиляторы, которые я знаю, поддерживают его, по крайней мере, на ПК, как расширение стандарта.)

Поэтому перед использованием a вам нужно будет написать a=0 или сделать a инициализированным членом объединения, поскольку a не имеет общей последовательности инициализации ни с b, ни с c.

Итак, , если вы используете memset, также предлагаемый в ответе MSalters, что бы вы ни делали, вам придется назначить что-то члену союза перед его использованием. Если вы хотите оставаться в определенном поведении, не используйте memset. Обратите внимание, что memset можно безопасно использовать со стандартным объектом макета, который не является членом союза, поскольку их жизненный цикл начинается, когда для них получено хранилище.


В заключение, чтобы остаться в определенном поведении, вы должны хотя бы инициализировать один элемент, тогда вы можете проверить других членов профсоюза, которые имеют общую последовательность инициализации с инициализированным членом.

  • Если вы намерены использовать анонимный союз в основной функции, вы можете объявить статический союз: все статические объекты инициализируются нулем. (Но не переинициализируются, когда вы вспоминаете функцию, которая не будет выполняться с main()):

    int main(){
     static union {
      s b;
      int a;
      char c;
      };
     //...
     }
    

    Как описано в стандарте С++ §8.6, статья (6.3) [dcl.init]:

    если T является (возможно, cv-квалифицированным) типом объединения, объекты первого нестатического именованного элемента данных равны нулю, инициализируется и заполняется инициализируется нулевыми битами;

  • В противном случае, если между элементами структур не существует (s), вы можете агрегировать инициализацию с пустым списком большего члена (s):

    //...
    int main(){
      union {
       int a;
       s b{};
       char c;
       };
      //...
      }
    

    Эта работа, потому что все члены профсоюзов выровнены. Поэтому, если между членами s не существует прокладки, каждый байт памяти объединения будет инициализирован нолем, стандарт С++ §9.3 [class.union] статья 2:

    Размер объединения достаточен для того, чтобы содержать самый большой из его нестатических элементов данных. Каждая нестатическая информация член распределяется так, как если бы он был единственным членом структуры. [Примечание: объект объединения и его нестатические данные члены являются взаимопереключаемыми с указателем (3.9.2, 5.2.9). Как следствие, все нестатические члены данных объединения объект имеет тот же адрес.

  • Если внутри S есть надстройка, просто объявите массив char для цели инициализации:

    //...
    int main(){
      union {
       char _initialization[sizeof(s)]{};
       int a;
       s b;
       char c;
       };
      //...
      }
    

Примечание. Используя ваш пример или два последних примера кода, а код с помощью memset создает тот же самый набор инструкций для инициализации (clang → x86_64):

    pushq   %r14
    pushq   %rbx
    subq    $120, %rsp
    xorps   %xmm0, %xmm0
    movaps  %xmm0, 96(%rsp)
    movaps  %xmm0, 80(%rsp)
    movaps  %xmm0, 64(%rsp)
    movaps  %xmm0, 48(%rsp)
    movaps  %xmm0, 32(%rsp)
    movaps  %xmm0, 16(%rsp)
    movq    $0, 109(%rsp)

Ответ 2

Просто memset каждый член и рассчитывайте на оптимизатор, чтобы устранить избыточные записи.

Ответ 3

Я просто разделяю простуду, возможно, мы можем использовать метапрограммирование следующим образом:

template<typename T1, typename T2>
struct Bigger
{
  typedef typename std::conditional<sizeof(T1) >= sizeof(T2), T1, T2>::type Type;
};

// Recursion helper
template<typename...>
struct BiggestHelper;

// 2 or more types
template<typename T1, typename T2, typename... TArgs>
struct BiggestHelper<T1, T2, TArgs...>
{
    typedef typename Bigger<T1, typename BiggestHelper<T2, TArgs...>::Type>::Type Type;
};

// Exactly 2 types
template<typename T1, typename T2>
struct BiggestHelper<T1, T2>
{
    typedef typename Bigger<T1, T2>::Type Type;
};

// Exactly one type
template<typename T>
struct BiggestHelper<T>
{
    typedef T Type;
};

template<typename... TArgs>
struct Biggest
{
    typedef typename BiggestHelper<TArgs...>::Type Type;
};

Итак, в главном fucntion мы можем сделать так:

std::memset(&b, 0, sizeof(Biggest<int,s,char>::Type));