¿Alguna vez es malo marcar una función C ++ constexpr?

26

Dada una función muy trivial,

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

Debería ser muy obvio que es fácil convertir esta función en una constexprfunción, lo que me permite usarla al definir constexprvariables, así:

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

Mi suposición es que esto es estrictamente una mejora, ya que la función todavía se puede llamar en un constexprcontexto diferente, y ahora también se puede usar para ayudar a definir variables constantes de tiempo de compilación.

Mi pregunta es, ¿hay situaciones en las que esta sea una mala idea? Por ejemplo, al hacer esta función constexpr, ¿puedo encontrarme en una situación en la que esta función ya no sea utilizable en una circunstancia particular, o donde se porte mal?

Xirema
fuente
1
Lo único en lo que podía pensar era en los errores del compilador. Es posible que una llamada recursiva a la función constexpr pueda causar un paso de compilación realmente lento o incluso un compilador sin memoria.
Zan Lynx

Respuestas:

19

Esto solo importa si la función es parte de una interfaz pública y desea mantener versiones futuras de su API compatible con binarios. En ese caso, debe pensar detenidamente cómo desea evolucionar su API y dónde necesita puntos de extensión para futuros cambios.

Eso hace que un constexprcalificador sea una decisión de diseño irrevocable. No puede eliminar este calificador sin un cambio incompatible en su API. También limita cómo puede implementar esa función, por ejemplo, no podrá realizar ningún registro dentro de esta función. No todas las funciones triviales seguirán siendo triviales en la eternidad.

Eso significa que debe usarlo preferiblemente constexprpara funciones que son inherentemente funciones puras, y que serían realmente útiles en tiempo de compilación (por ejemplo, para la metaprogramación de plantillas). No sería bueno hacer las funciones constexpr solo porque la implementación actual resulta ser constexpr-able.

Cuando la evaluación en tiempo de compilación no sea necesaria, el uso de funciones en línea o funciones con enlace interno parecería más apropiado que eso constexpr. Todas estas variantes tienen en común que el cuerpo de la función es "público" y está disponible en la misma unidad de compilación que la ubicación de la llamada.

Si la función en cuestión no forma parte de una API pública estable, esto no es un problema, ya que puede cambiar arbitrariamente el diseño a voluntad. Pero como ahora controla todos los sitios de llamadas, no es necesario marcar una función constexpr "por si acaso". Usted sabe si se está utilizando esta función en un contexto constexpr. Agregar calificadores innecesariamente restrictivos podría considerarse ofuscación.

amon
fuente
12

Marcar una función constexprtambién la convierte en una función en línea § [dcl.constexpr] / 1:

Una función o miembro de datos estáticos declarado con el especificador constexpr es implícitamente una función o variable en línea (7.1.6).

inline, a su vez, significa que debe incluir la definición de esa función en cada unidad de traducción en la que se pueda utilizar. Eso básicamente significa que las constexprfunciones tienen que ser:

  1. restringido para uso en una unidad de traducción, o
  2. definido en un encabezado.

La mayoría de las funciones típicas que desea declarar en un encabezado y definir en un archivo fuente (y cualquier otra cosa que las use solo incluye el encabezado, luego los enlaces contra el archivo objeto de esa fuente) constexprsimplemente no funcionarán.

En teoría, supongo que podría mover todo a los encabezados y tener solo un archivo fuente que solo incluya todos los encabezados, pero esto dañaría drásticamente los tiempos de compilación, y para la mayoría de los proyectos serios requeriría una inmensa cantidad de memoria para compilar.

Una constexprfunción también está restringida de alguna manera, por lo que para algunas funciones puede no ser una opción en absoluto. Las restricciones incluyen:

  1. las funciones virtuales no pueden ser constexpr.
  2. su tipo de retorno debe ser un 'tipo literal "(p. ej., no objetos con ctors o dtors no trival).
  3. Todos sus parámetros deben ser de tipo literal.
  4. El cuerpo de la función no puede contener un trybloque.
  5. no puede contener una definición variable de un tipo no literal, ni nada con duración de almacenamiento estático o de subprocesos.

Me he saltado un par de cosas bastante oscuras (por ejemplo, tampoco puede contener una gotoo una asmdeclaración), pero entiendes la idea: para bastantes cosas, simplemente no funcionará.

En pocas palabras: sí, hay bastantes situaciones en las que esta sería una mala idea.

Jerry Coffin
fuente
"no debe ser virtual (hasta C ++ 20)" Me pregunto cómo una función virtual puede ser constexpr. ¿Qué hacen los compiladores?
Chaosink