É possível alterar um membro ativo em uma união copiando o valor do membro ativo anterior usando std::construct_at
?
Este exemplo mínimo
#include <memory>
constexpr int f() {
union U {
char x{0};
int y;
} u;
std::construct_at(&u.y, u.x);
return u.y;
}
static_assert( f() == 0 );
é aceito pelo GCC e MSVC, mas o Clang o rejeita com o erro:
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__memory/construct_at.h:41:50:
note: read of member 'x' of union with active member 'y' is not allowed in a constant expression
41 | return ::new (std::__voidify(*__location)) _Tp(std::forward<_Args>(__args)...);
Demonstração online: https://gcc.godbolt.org/z/xrj57jroj
Qual implementação está correta aqui?
A resolução do CWG 2721 esclarece que o armazenamento é considerado reutilizado
new
quando a função de alocação retorna e antes donew
inicializador ser avaliado.Quando o armazenamento é reutilizado, a vida útil do objeto anterior termina e ele não fica mais ativo.
Portanto, Clang está correto.
std::construct_at
pega argumentos por referência e os encaminha para onew
inicializador. A conversão lvalue para rvalue é aplicadau.x
para obter o valor armazenado somente durante a avaliação do inicializador. Neste ponto, o armazenamento já foi reutilizado e o tempo de vida deu.x
terminou.Portanto, a
std::construct_at
chamada não é permitida em uma expressão constante e tem comportamento indefinido fora de uma.Isso pode ser corrigido escrevendo
ou
em vez de.