C++26: Los bucles infinitos triviales ya no son comportamiento indefinido
C++26 cambia la forma en que el lenguaje trata los bucles infinitos “triviales” como `while (true);`, que antes eran comportamiento indefinido y podían optimizarse de formas sorprendentes, llegando incluso a hacer que el flujo de control cayera en código no relacionado. Los comentaristas discuten si esta regla de larga data tenía sentido alguna vez, señalando usos reales en código embebido y de kernel donde estos bucles se usan para detenerse ante errores o esperar interrupciones. La decisión del nuevo estándar de modelar estos bucles como llamadas a `std::this_thread::yield()` se ve por algunos como una forma pragmática de satisfacer los requisitos de “progreso hacia adelante”, y por otros como una fuente innecesaria de llamadas al sistema ocultas y complejidad en un lenguaje de programación de sistemas ya tensionado por el comportamiento indefinido.
Alcance del cambio y comportamiento sorprendente
- Muchos comentaristas se sorprenden de que los bucles infinitos triviales fueran UB durante “casi 1/6 de siglo”, lo que provocaba cosas como:
- Funciones que “caían” en funciones posteriores cuando el compilador elimina el bucle y el epílogo.
- Funciones no-void sin
returnque simplemente continúan en otro código.
- Algunos consideran que esto viola de forma importante las expectativas intuitivas al depurar (“quita código hasta que desaparezca el problema” falla si un bucle mínimo puede hacer cualquier cosa).
Por qué los bucles infinitos eran UB (optimización / progreso hacia adelante)
- Varias publicaciones enlazan esto con las “garantías de progreso hacia adelante”: los compiladores pueden asumir que los hilos eventualmente hacen progreso (return, E/S, volatile, atómicos).
- La UB en bucles infinitos permitía:
- Fusionar o reordenar bucles sin probar terminación.
- Transformaciones como elevar stores por encima de cálculos puros.
- Algunos no están convencidos de que estas optimizaciones merezcan la rareza semántica, y dicen que se trata más de concursos de benchmarks que de beneficios reales para el usuario.
Casos de uso embebidos y de sistemas
- Hay un fuerte desacuerdo sobre que “nunca hay una buena razón” para bucles infinitos triviales.
- Desarrolladores de embebidos y bare-metal citan:
- Manejadores de detención por error.
- “Super bucles” de
mainen sistemas impulsados por interrupciones. - Permanecer despierto en lugar de costosas transiciones de suspensión/activación.
- Otros argumentan que se deberían usar instrucciones dedicadas de detener/suspender la CPU o bucles que indiquen error (p. ej., códigos de parpadeo), no giros vacíos.
Nuevo comportamiento en C++26 (std::this_thread::yield)
- La nueva regla define un caso muy estrecho: un bucle infinito trivialmente vacío con condición constante.
- Las críticas se centran en:
- Reemplazarlo por una llamada implícita a
this_thread::yield(), es decir, una llamada al sistema invisible en código que parece libre de efectos secundarios. - Riesgo en entornos freestanding / embebidos / aislados o en tiempo real.
- Inconsistencia:
while(true);vswhile(true) continue;vs otras formas de escribirlo con semánticas distintas.
- Reemplazarlo por una llamada implícita a
- Algunos prefieren esto antes que UB, pero aun así lo llaman “horrible” y preferirían ver:
- Un diagnóstico/advertencia.
- Un primitivo de la biblioteca estándar “detener aquí”.
- O la regla al estilo C11 (los bucles infinitos con condición constante simplemente están definidos como no terminantes).
Preocupaciones más amplias sobre UB y la dirección de C++
- Largo subhilo sobre la UB como herramienta de optimización frente a ser una fuente de miscompilaciones y comportamiento incomprensible.
- Comparaciones con C y Rust:
- El tratamiento especial de C para bucles con expresión constante se ve como más sensato.
- Se menciona
loop {}de Rust y la historia de LLVM como presión previa para corregir esto.
- Varios comentarios lamentan el creciente comportamiento oculto y la complejidad de C++ moderno, y expresan temor de que el uso freestanding / embebido siga volviéndose más difícil.