Alterando o membro ativo da união em expressões constantes

Tocando com constexpr e union Descobri que não posso alterar o membro ativo de uma union em constexpr . Apenas uma exceção: union de classs vazias.

 constexpr bool t() { struct A {}; struct B {}; union U { A a; B b; } u{}; ua = A{}; ub = B{}; return true; } static_assert(t()); constexpr bool f() { struct A { char c; }; struct B { char c; }; union U { A a; B b; } u{}; ua = A{}; ub = B{}; // error originating from here return true; } static_assert(f()); 

A primeira function pode produzir expressão constante. Mas o segundo não pode. Erro rígido diz:

 main.cpp:23:15: error: static_assert expression is not an integral constant expression static_assert(f()); ^~~ main.cpp:20:11: note: assignment to member 'b' of union with active member 'a' is not allowed in a constant expression ub = B{}; ^ main.cpp:20:9: note: in call to '&u.b->operator=(B{})' ub = B{}; ^ main.cpp:23:15: note: in call to 'f()' static_assert(f()); ^ 1 error generated. 

EXEMPLO AO VIVO

1.) É possível alterar o membro ativo da union em expressões constantes?

Eu tentei destruir membro ativo, mas não é permitido, devido a destruidores não são constexpr em geral. Também tentei usar o operator new posicionamento operator new ( ::new (&u.b) B{2}; ), mas também unusccessfull. reinterpret_cast também não é permitido em expressões constantes. Alterar membros da subsequência inicial comum também é proibido.

2.) Existem maneiras de tornar o boost::variant literal mutável (no sentido de alterar o tipo alternativo ativo) boost::variant tipo boost::variant ? Como se parece com o armazenamento, se possível?

3.) É um comportamento indefinido para designar membros não ativos da union de tipos atribuíveis à cópia trivialmente em tempo de execução ? É um comportamento indefinido para construir um membro não ativo da union de tipos trivialmente copiáveis ​​usando o operator new posicionamento evitando a destruição preliminar do membro ativo no tempo de execução ?

ADICIONAL:

Eu posso mudar toda a union tipo literal, mas não seu membro não ativo:

 constexpr bool f() { struct A { char c; }; struct B { char c; }; union U { A a; B b; constexpr U(A _a) : a(_a) { ; } constexpr U(B _b) : b(_b) { ; } }; U a(A{}); aa = A{}; // check active member is A U b(B{}); bb = B{}; // check active member is B a = b; a = B{}; // active member is B! return true; } static_assert(f()); 

EXEMPLO AO VIVO

Portanto, para o tipo literal de variant do operador de atribuição de conversão de tipos que podem ser copiados trivialmente, seria template constexpr variant & operator = (T && x) { return *this = variant(std::forward(x)); } template constexpr variant & operator = (T && x) { return *this = variant(std::forward(x)); } .

Disclaimer: “ativo” é definido em P0137R0 .

É possível alterar o membro ativo da união em expressões constantes?

Não diretamente, desde que a modificação de um membro não ativo é proibida – [expr.const] / (2.8):

– uma conversão de valor para preço (4.1) ou modificação (5.18, 5.2.6, 5.3.2) que é aplicada a um valor gl que se refere a um membro não ativo de uma união ou a um subobject do mesmo;

No entanto, este texto parece defeituoso, uma vez que é realmente possível “modificar” um membro não ativo por atribuição de outro object de união, como mostrado no seu exemplo. Na verdade, o operador de atribuição de cópia executa uma cópia dos bytes subjacentes e as informações internas sobre o membro ativo:

O operador de atribuição de cópia definido implicitamente para uma união X copia a representação de object (3.9) de X


É um comportamento indefinido para designar membros não ativos de união de tipos atribuíveis a cópia trivialmente em tempo de execução?

Isso é presumivelmente bom para objects de um tipo de class que pode ser copiado trivialmente, já que eles têm destruidores triviais e construtores de cópia / operadores de atribuição. Embora não especificado, o CWG # 1116 parece implicar que se destina a funcionar:

Nós nunca dizemos o que é o membro ativo de um sindicato, como ele pode ser mudado, e assim por diante. A Norma não deixa claro se o seguinte é válido:

 union U { int a; short b; } u = { 0 }; int x = ua; // presumably this is OK, but we never say that a is the active member ub = 0; // not clear whether this is valid