Qual é a razão para std :: memcpy não ser constexpr mesmo em C ++ 20?

Sep 24 2020

Eu entendo que copiar pedaços arbitrários de memória nem sempre é possível fazer em tempo de compilação, mas já que estamos obtendo constexpr constantes, métodos virtuais e também algoritmos, por que não memcpy também? É também uma espécie de algoritmo.

Além disso,

  • C ++ 20 std::bit_castparece uma std::memcpysolução alternativa, reinterpret_castmas é constexpr.
  • std::copyo uso de iteradores é marcado como constexprC ++ 20, portanto, a cópia é de alguma forma possível para tipos.

O uso seria copiar ou apenas "reinterpretar" variáveis ​​/ arrays em constexprfunções, o primeiro não é resolvido pelo std::bit_castAFAIK. Em particular, a pergunta e minha resposta gostariam de usá-lo.

  • Existe alguma razão particular para por que std::bit_castpode ser constexpr, mas std::memcpynão pode?
  • Tem a ver com memcpy usando ponteiros void em vez de referências digitadas?
  • Na verdade, não precisa copiar nada?
  • Compatibilidade com versões anteriores do C?
  • Talvez porque não há suporte para um "ponteiro para memória constexpr"? Mas o mesmo se aplica ao parâmetro de referência em std::bit_caste aos iteradores em std::copy.

Uma resposta relevante para C ++ 20 bit_cast vs reinterpret_cast cita brevemente de algum lugar:

Além disso, atualmente é impossível implementar uma função bit cast constexpr, já que memcpy em si não é constexpr. Marcar a função proposta como constexpr não requer ou impede que memcpy se torne constexpr, mas requer suporte do compilador. Isso deixa as implementações livres para usar sua própria solução interna (por exemplo, o LLVM tem um opcode bitcast).

Mas não entra em detalhes de não torná-lo constexpr também.

Observe que eu não pergunto por que std::bit_castexiste. Eu gosto disso, fornece uma intenção clara em vez de uma std::memcpysolução alternativa.

Respostas

2 NicolBolas Sep 24 2020 at 20:49

O modelo de objeto C ++ no código de tempo de execução geralmente é tratado de forma um tanto vaga. Possui regras bastante rígidas, mas há um monte de backdoors que são permitidos ou declarados UB. O último significa que você ainda pode escrever código para fazer isso, mas C ++ não garante nada sobre o comportamento desse código.

Na avaliação constante (também conhecida como: execução de código em tempo de compilação), esse não é o caso. As restrições constexprsão especificamente destinadas a permitir que o modelo de objeto seja uma coisa real que você deve seguir, sem backdoors viáveis. E mesmo aqueles que ele ocasionalmente permite são explicitamente solicitados a ser malformados e produzir um erro de compilação, ao invés de serem UB silenciosos.

Basicamente, em tempo de execução, você trata a memória apenas como bytes de armazenamento. Em tempo de compilação, você não pode ; você não tem permissão para. Mesmo com a alocação dinâmica no constexprcódigo adicionado ao C ++ 20, você não consegue jogar muitos dos jogos que costuma jogar com esse tipo de coisa.

memcpylida com bytes de armazenamento, copiando-os para frente e para trás sem ter ideia do que significam. bitcast conhece os objetos de origem e de destino e não permitirá que você faça isso, a menos que os objetos de origem e destino sejam apropriados para o bitcasting (isto é: trivialmente copiável).

bitcasttambém tem restrições muito específicas sobre o conteúdo de ambos os objetos, se você quiser que funcionem em tempo de compilação. Em particular, você não pode bitcastponteiros ou quaisquer objetos que contenham ponteiros de qualquer tipo. Ou referências.

Isso ocorre porque os ponteiros em tempo de compilação não são apenas endereços. Para capturar UB, um ponteiro de tempo de compilação precisa saber o verdadeiro tipo dinâmico do objeto para o qual ele aponta. Portanto, conversões de ponteiro que apenas convertem o endereço não são permitidas em tempo de compilação.

1 t.niese Sep 24 2020 at 16:10

Isso é mais um comentário do que uma resposta, pois estou apenas citando o que está escrito em P0202R0: Adicionar modificadores Constexpr a funções em e cabeçalhos , mas escrevo aqui porque não cabe nos comentários:

B. std::memmovee std::memcpydeve ter adições constexpr
std::memmove e std::memcpyaceitar void*e const void*parâmetros. Isso os torna impossíveis de implementar em C ++ como puro constexpr, porque expressões constantes não podem avaliar uma conversão do tipo cv void *para um tipo de ponteiro para objeto de acordo com [expr.const].
No entanto, essas funções não são apenas populares, mas também são amplamente utilizadas na Biblioteca Padrão para obter melhor desempenho. Não torná-los constexpr forçará o desenvolvedor da Biblioteca padrão a ter intrínsecos do compilador para eles de qualquer maneira. Esta é uma etapa difícil que deve ser realizada.

A seção relacionada de [expr.const]:

8.6 Expressões constantes [expr.const]
[...] Uma expressão eé uma expressão constante central , a menos que a avaliação de e, seguindo as regras da máquina abstrata (6.8.1), avaliasse uma das seguintes expressões:
[...]
(2.13) - uma conversão do tipo cv void*para um tipo de ponteiro para objeto ;