Considere este trecho de código C++23:
struct MyData {
std::int32_t value;
};
std::vector<char> read_from_net();
void send_over_net(std::vector<char>&);
...
std::vector<char> buffer = read_from_net();
std::start_lifetime_as<MyData>(buffer.data())->value = 222; // (1)
send_over_net(buffer); // (2)
Assumindo que todos os requisitos para iniciar a vida útil de MyData
at buffer.data()
sejam respeitados:
- Existe alguma UB neste código? Começar o tempo de vida assim e modificar o campo é bem definido?
- Posso acessar o buffer original como caracteres
(2)
sem UB, considerando que issostart_lifetime_as
encerrou a vida útil do objeto anterior e iniciou uma nova? - Ou
start_lifetime_as
apenas inicia outra vida, mas também preserva a anterior? - Devo usar
std::launder(buffer.data())[0]
o método de acesso ao buffer original como uma matriz de caracteres depois(2)
?
TL;DR É um comportamento indefinido.
Parabéns por pensar nisso.
Os pontos principais são que
reutilizar o armazenamento para criar um novo objeto encerra a vida útil dos objetos antigos 1
https://eel.is/c++draft/basic.life#2.5
aliasing through
char
é permitidohttps://eel.is/c++draft/basic.lval#11.3
início da vida útil de um novo objeto de tipo diferente termina a validade de todos os ponteiros para qualquer parte desse objeto
https://eel.is/c++draft/basic.life#10
Você pode obter um novo ponteiro válido por meio
std::launder
de um ponteiro expirado existente ou por meioreinterpret_cast
de um ponteiro válido para o novo objeto (o ponteiro retornado destd::start_lifetime_as
).Com
std::launder
, o ponteiro retornado é válido, mas não restaura a validade de outros ponteiros expirados (como os que estão dentrostd::vector
dele). Ai.https://eel.is/c++draft/ptr.launder#5
1 A única regra que pode ser interpretada para permitir isso aparece aqui, na linguagem relativamente nova em torno
Se o novo objeto estiver "aninhado dentro" dos objetos antigos de propriedade de
std::array
, então seu tempo de vida continua, deixando tudo ok.https://eel.is/c++draft/intro.object#4
Mas essa permissão se aplica apenas a
unsigned char
estd::byte
, não simplesmente achar
, então não ajuda aqui.https://eel.is/c++draft/intro.object#3
1. Existe alguma UB neste código? O início do tempo de vida é assim e o campo de modificação está bem definido?
Supondo que você esteja cumprindo todos os requisitos, não deve haver nenhum comportamento indefinido.
if
buffer.data()
aponta para uma região de memória que satisfaça os requisitos de tamanho e alinhamento deMyData
, ostd::start_lifetime_as
iniciará com sucesso o tempo de vida de umMyData
objeto naquele local. No seu snippet, depois de iniciar o tempo de vida deMyData
embuffer.data()
, escrever em seu membro (ou seja, definir valor = 222) é uma operação definida.2. e 3. Sim, você pode acessar o original
buffer
emchar
(2) sem comportamento indefinido.4. Em C++ 17 std::launder pode ser usado para obter um ponteiro para um objeto que pode ter sido realocado. Se você quiser otimizar algo, pode usá-lo. Do meu ponto de vista, você não precisa dele porque já tem um ponteiro válido de
buffer.data()
.