É sempre ruim marcar uma função C ++ constexpr?

26

Dada uma função muito trivial,

int transform(int val) {
    return (val + 7) / 8;
}

Deveria ser muito óbvio que é fácil transformar essa função em uma constexprfunção, permitindo que eu a use ao definir constexprvariáveis, assim:

constexpr int transform(int val) {
    return (val + 7) / 8;
}

Minha suposição é que isso é estritamente uma melhoria, já que a função ainda pode ser chamada em um constexprcontexto fora do contexto, e agora também pode ser usada para ajudar a definir variáveis ​​constantes em tempo de compilação.

Minha pergunta é : existem situações em que essa é uma má ideia? Como, ao fazer essa função constexpr, posso encontrar uma situação em que essa função não será mais utilizável em uma circunstância específica ou onde ela se comportará mal?

Xirema
fonte
11
A única coisa que eu conseguia pensar eram nos erros do compilador. É possível que uma chamada de função constexpr recursiva possa causar uma etapa de compilação muito lenta ou até mesmo uma falha de memória no compilador.
Zan Lynx

Respostas:

19

Isso importa apenas se a função fizer parte de uma interface pública e você desejar manter versões futuras da sua API compatível com binários. Nesse caso, você deve pensar cuidadosamente em como deseja evoluir sua API e onde precisa de pontos de extensão para futuras alterações.

Isso faz um constexpr qualificador uma decisão irrevogável de design. Você não pode remover este qualificador sem uma alteração incompatível na sua API. Também limita como você pode implementar essa função, por exemplo, você não seria capaz de fazer nenhum log dentro dessa função. Nem toda função trivial permanecerá trivial na eternidade.

Isso significa que você deve preferencialmente usar constexprpara funções que são inerentemente puras e que seriam realmente úteis em tempo de compilação (por exemplo, para metaprogramação de modelos). Não seria bom tornar as funções constexpr apenas porque a implementação atual é passível de ser constexprable.

Onde a avaliação em tempo de compilação não é necessária, o uso de funções em linha ou funções com ligação interna parece mais apropriado constexpr. Todas essas variantes têm em comum que o corpo da função é "público" e está disponível na mesma unidade de compilação que o local da chamada.

Se a função em questão não fizer parte de uma API pública estável, isso é um problema menor, pois você pode alterar arbitrariamente o design à vontade. Mas como agora você controla todos os sites de chamada, não é necessário marcar uma função constexpr "apenas por precaução". Você sabe se está usando esta função em um contexto constexpr. Adicionar qualificadores desnecessariamente restritivos pode ser considerado ofuscamento.

amon
fonte
12

Marcar uma função como constexprtambém a torna uma função embutida § [dcl.constexpr] / 1:

Uma função ou membro de dados estático declarado com o especificador constexpr é implicitamente uma função ou variável embutida (7.1.6).

inline, por sua vez, significa que você precisa incluir a definição dessa função em todas as unidades de tradução nas quais ela pode ser usada. Isso basicamente significa que as constexprfunções devem ser:

  1. restrito ao uso em uma unidade de tradução, ou
  2. definido em um cabeçalho.

As funções mais comuns que você deseja declarar em um cabeçalho e definir em um arquivo de origem (e qualquer outra coisa que as use apenas inclui o cabeçalho e, em seguida, os links no arquivo de objeto dessa fonte) constexprsimplesmente não funcionam.

Em teoria, suponho que você possa simplesmente mover tudo para os cabeçalhos e ter apenas um arquivo de origem que inclua todos os cabeçalhos, mas isso prejudicaria drasticamente o tempo de compilação e, para os projetos mais sérios, seria necessária uma quantidade imensa de memória para compilar.

Uma constexprfunção também é restrita em alguns aspectos, portanto, para algumas funções, pode não ser uma opção. As restrições incluem:

  1. funções virtuais não podem ser constexpr.
  2. seu tipo de retorno deve ser um 'tipo literal "(por exemplo, nenhum objeto com doutores ou dicionários não trival).
  3. todos os seus parâmetros devem ser tipos literais.
  4. o corpo da função não pode conter um trybloco.
  5. não pode conter uma definição variável de um tipo não literal ou qualquer coisa com duração de armazenamento estático ou de encadeamento.

Eu pulei algumas coisas obscuras (por exemplo, ela também não pode conter uma gotoou uma asmdeclaração), mas você entendeu - para algumas coisas, simplesmente não vai funcionar.

Conclusão: sim, existem algumas situações em que isso seria uma péssima idéia.

Jerry Coffin
fonte
"não deve ser virtual (até C ++ 20)" Estou pensando em como uma função virtual pode ser constexpr? O que os compiladores fazem?
chaosink 30/07