C++ tem a regra de "sequência inicial comum" para uniões:
Em uma união de layout padrão com um membro ativo (11.5) do tipo struct T1, é permitido ler um membro de dados não estático m de outro membro de união do tipo struct T2, desde que m faça parte da sequência inicial comum de T1 e T2; o comportamento é como se o membro correspondente de T1 fosse nomeado.
Como isso interage com structs aninhados ? Por exemplo, é permitido acessar u.s.s1.a1
, u.s.s1.a2
, u.s.s2.a1
, u.s.s2.a2
se u.b
estiver ativo? Ou o fato de S::s1
e B::b1
terem tipos diferentes implica que S
e B
não têm uma sequência inicial comum ?
struct A
{
int a1, a2;
};
struct S
{
A s1, s2;
};
struct B
{
int b1, b2, b3, b4;
};
union U
{
S s;
B b;
};
/******************/
U u;
u.s.s1.a1 = 1;
u.b.b1 == 1; // UB?
/******************/
As exceções comuns de sequência inicial para acesso de membro union inativo são bem específicas. Elas efetivamente exigem definições de membro não estático exatamente idênticas.
Especificamente, [class.mem.general]/23 requer que, na ordem de declaração, os membros correspondentes tenham tipos compatíveis com o layout .
Isso, por sua vez, é definido em [basic.types.general]/11 e pode ser aplicado somente se ambos os tipos forem tipos de classe ou nenhum for.
Como
A
eint
não são tipos de classe ou não classe, eles não são compatíveis com o layout e, portanto, a sequência comum inicial deS
eB
é vazia.Então o acesso tem UB.
Não é nem garantido para nenhum
int
membro, exceto os primeiros (indiretos), que eles tenham o mesmo deslocamento da classe que os contém. Uma implementação em conformidade teria permissão para adicionar padding após o primeiroint
emA
, mas não emB
.Da mesma forma, uma implementação pode escolher
alignof(A) != alignof(B)
.Claro, nenhuma dessas opções é prática, então as implementações não fazem isso.