Al final de la charla de Scott Schurr "Introducing constexpr
" en CppCon , pregunta "¿Hay alguna manera de envenenar una función"? Luego explica que esto se puede hacer (aunque de una manera no estándar) mediante:
- Poner un
throw
en unaconstexpr
función - Declarando un no resuelto
extern const char*
- Haciendo referencia a los no resueltos
extern
en elthrow
Siento que estoy un poco fuera de mi alcance aquí, pero tengo curiosidad:
- ¿Qué significa "envenenar una función"?
- ¿Cuál es la importancia / utilidad de la técnica que describe?
constexpr
función se evalúe en tiempo de compilación.constexpr
función podría usarse en tiempo de compilación o en tiempo de ejecución. Entonces, ¿esta es una forma de forzarlo para que no pueda usarlo en tiempo de ejecución? ¿Cuándo es eso útil?constexpr
función a menudo no es la implementación más eficiente debido a las restricciones, por lo que es posible que no desee evaluarla en tiempo de ejecución; o tal vez sea el caso de error (como en su ejemplo).Respuestas:
En general, se refiere a inutilizar una función, por ejemplo, si desea prohibir el uso de la asignación dinámica en un programa, podría "envenenar" la
malloc
función para que no se pueda usar.En el video lo está usando de una manera más específica, lo cual queda claro si lees la diapositiva que se muestra cuando habla de envenenar la función, que dice "¿Una forma de forzar solo el tiempo de compilación?"
Así que está hablando de "envenenar" la función para hacerla incalculable en tiempo de ejecución, por lo que solo se puede llamar en expresiones constantes. La técnica es tener una rama en la función que nunca se toma cuando se llama en un contexto de tiempo de compilación, y hacer que esa rama contenga algo que causará un error.
Se
throw
permite una expresión en una función constexpr, siempre que nunca se alcance durante las invocaciones de la función en tiempo de compilación (debido a que no puede lanzar una excepción en tiempo de compilación, es una operación inherentemente dinámica, como la asignación de memoria). Por lo tanto, una expresión de lanzamiento que se refiere a un símbolo indefinido no se usará durante las invocaciones en tiempo de compilación (porque no se compilará) y no se puede usar en tiempo de ejecución, porque el símbolo indefinido causa un error de enlazador.Debido a que el símbolo indefinido no es "usado por odr" en las invocaciones en tiempo de compilación de la función, en la práctica el compilador no creará una referencia al símbolo, por lo que está bien que no esté definido.
¿Eso es útil? Está demostrando cómo hacerlo, sin decir necesariamente que sea una buena idea o que sea muy útil. Si tiene la necesidad de hacerlo por alguna razón, entonces su técnica podría resolver su problema. Si no lo necesita, no tiene que preocuparse por ello.
Una razón por la que podría ser útil es cuando la versión en tiempo de compilación de alguna operación no es tan eficiente como podría ser. Existen restricciones sobre el tipo de expresiones permitidas en una función constexpr (especialmente en C ++ 11, algunas restricciones se eliminaron en C ++ 14). Por lo tanto, es posible que tenga dos versiones de una función para realizar un cálculo, una que es óptima, pero usa expresiones que no están permitidas en una función constexpr, y otra que es una función constexpr válida, pero que funcionaría mal si se la llama en la ejecución. hora. Podría envenenar el subóptimo para asegurarse de que nunca se use para llamadas en tiempo de ejecución, asegurando que la versión más eficiente (no constexpr) se use para llamadas en tiempo de ejecución.
NB El rendimiento de una función constexpr utilizada en tiempo de compilación no es realmente importante, porque de todos modos no tiene sobrecarga de tiempo de ejecución. Puede ralentizar su compilación haciendo que el compilador haga un trabajo adicional, pero no tendrá ningún costo de rendimiento en tiempo de ejecución.
fuente
'Envenenar' un identificador significa que cualquier referencia al identificador después del 'envenenamiento' es un error del compilador. Esta técnica se puede usar, por ejemplo, para una desaprobación total (la función ESTÁ desaprobada, ¡nunca la use!).
En GCC tradicionalmente había un pragma para esto:
#pragma GCC poison
.fuente