C++ Moderno
Guía para escribir C++ moderno usando modismos de C++20, C++23 y C++26. Se centra en patrones que eliminan clases de vulnerabilidades y reducen código repetitivo, con un énfasis en la seguridad de Trail of Bits.
Cuándo Usar Esta Habilidad
- Escribir nuevas funciones, clases o bibliotecas en C++
- Modernizar código C++ existente (patrones pre-C++20)
- Elegir entre enfoques heredados y modernos
- Trabajar en C++ crítico para la seguridad o la seguridad operacional
- Revisar código C++ para adoptar modismos modernos
Cuándo NO Usar Esta Habilidad
- El usuario requiere explícitamente un estándar anterior: Respeta las restricciones (embedded, ABI heredado)
- Código C puro: Esta habilidad es específica de C++
- Preguntas sobre sistemas de construcción: La configuración de CMake, Meson, Bazel está fuera del alcance
- Proyectos que no son C++: Bases de código mixtas donde C++ no es el principal
Anti-patrones a Evitar
| Evitar | Usar en su lugar | Por qué |
|---|---|---|
new/delete | std::make_unique, std::make_shared | Elimina fugas, doble liberación |
| Punteros crudos propietarios | std::unique_ptr, std::shared_ptr | Semántica de propiedad RAII |
Arreglos C (int arr[N]) | std::array<int, N> | Consciente de límites, semántica de valor |
| Parámetros puntero + longitud | std::span<T> | No propietario, comprobable de límites |
printf / sprintf | std::format, std::print | Seguro en tipos, sin desbordamiento de búfer |
Casts estilo C (int)x | static_cast<int>(x) | Intención explícita, auditable |
Constantes #define | Variables constexpr | Con alcance, tipadas, depurables |
SFINAE / enable_if | Conceptos + requires | Restricciones legibles y errores claros |
| Códigos de error + parámetros de salida | std::expected<T, E> | Errores componibles y seguros en tipos |
union | std::variant | Seguro en tipos, sin UB silencioso |
mutex.lock()/unlock() crudo | std::scoped_lock | Seguro ante excepciones, sin interbloqueos |
std::thread | std::jthread | Unión automática, soporte de token de parada |
Macro assert() | contract_assert (C++26) | Visible para herramientas, configurable |
| CRTP manual | Deducir this (C++23) | Más simple, sin código repetitivo de plantillas |
| Generación de código con macros | Reflexión (C++26) | Sin sobrecarga, componible |
Consulta anti-patterns.md para la tabla completa (30+ patrones).
Árbol de Decisión
¿Qué estás haciendo?
|
+-- ¿Escribiendo nuevo código C++?
| +-- Usa modismos modernos por defecto (C++20/23)
| +-- Elige el estándar más nuevo que tu compilador soporte
| +-- Consulta los Niveles de Características a continuación
|
+-- ¿Modernizando código existente?
| +-- Empieza con los reemplazos del Nivel 1 (C++20/23)
| +-- Prioriza por impacto en seguridad (memoria > tipos > estilo)
| +-- Consulta anti-patterns.md para la tabla de migración
|
+-- ¿Código crítico para la seguridad?
| +-- Habilita las banderas de endurecimiento del compilador (ver más abajo)
| +-- Habilita el modo hardened de libc++
| +-- Ejecuta sanitizadores en CI
| +-- Consulta safe-idioms.md y compiler-hardening.md
|
+-- ¿Usando características de C++26?
+-- Reflexión: SÍ, planifícalo (GCC 16+)
+-- Contratos: con cautela, para nuevos límites de API
+-- std::execution: espera a la madurez del ecosistema
+-- Consulta cpp26-features.md
Niveles de Características
Las características están clasificadas por usabilidad práctica hoy, no por versión del estándar.
Nivel 1: Usar Hoy (C++20/23, soporte sólido del compilador)
| Característica | Reemplaza | Estándar |
|---|---|---|
Conceptos + requires | SFINAE, enable_if | C++20 |
| Ranges + vistas | Bucles iteradores crudos | C++20 |
std::span<T> | Puntero + longitud | C++20 |
std::format | sprintf, cadenas iostream | C++20 |
Comparación de tres vías <=> | Operadores de comparación manuales | C++20 |
std::jthread | std::thread + unión manual | C++20 |
| Inicializadores designados | Inicialización posicional de struct | C++20 |
std::expected<T,E> | Códigos de error, excepciones en límites | C++23 |
std::print / std::println | printf, std::cout << | C++23 |
Deducir this | CRTP, duplicación const/no-const | C++23 |
std::flat_map | std::map para uso intensivo de lectura | C++23 |
std::optional mónada | Comprobaciones if anidadas en optional | C++23 |
Consulta cpp20-features.md y cpp23-features.md.
Nivel 2: Desplegar Ahora (sin necesidad de subir el estándar)
Estos mejoran la seguridad sin cambiar la versión del estándar C++:
- Banderas de endurecimiento del compilador —
-D_FORTIFY_SOURCE=3,-fstack-protector-strong,-ftrivial-auto-var-init=zero - libc++ endurecido —
-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FASTpara comprobación de límites con ~0.3% de sobrecarga - Sanitizadores en CI — ASan + UBSan como mínimo; TSan para código concurrente
- Banderas de advertencia —
-Wall -Wextra -Wpedantic -Werror
Consulta compiler-hardening.md.
Nivel 3: Planificar (C++26, vale la pena reestructurar alrededor)
Reflexión es la característica más transformadora de C++26. Elimina:
- Código repetitivo de serialización (una función genérica reemplaza
to_jsonpor estructura) - Generadores de código (código de protobuf, Qt MOC)
- Trucos de registro basados en macros y conversión de enumeración a cadena
GCC 16 (abril de 2026) tiene reflexión fusionada. Planea que el nuevo código se beneficie de ella.
Nivel 4: Observar (C++26, necesita maduración)
- Contratos (
pre/post/contract_assert) — Mejores queassert(), pero sin soporte para funciones virtuales y soporte limitado del compilador. Adóptalos con cautela para nuevos límites de API. - std::execution (senders/receivers) — Marco de trabajo asíncrono potente, pero con una curva de aprendizaje pronunciada, sin planificador incluido y documentación deficiente. Espera a la madurez del ecosistema.
Consulta cpp26-features.md.
Referencia Rápida de Endurecimiento del Compilador
Banderas Esenciales (GCC + Clang)
-Wall -Wextra -Wpedantic -Werror
-D_FORTIFY_SOURCE=3
-fstack-protector-strong
-fstack-clash-protection
-ftrivial-auto-var-init=zero
-fPIE -pie
-Wl,-z,relro,-z,now
Específicas de Clang
-Wunsafe-buffer-usage
libc++ Endurecido (solo Clang/libc++)
-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST
Google desplegó esto en Chrome y en su flota de servidores: ~0.3% de sobrecarga, más de 1000 errores encontrados, 30% de reducción en fallos de segmentación en producción.
Consulta compiler-hardening.md para la guía completa.
Racionalizaciones a Rechazar
| Racionalización | Por Qué Está Mal |
|---|---|
| "Compila sin advertencias" | Las advertencias dependen de las banderas que habilitas. Añade -Wall -Wextra -Wpedantic. |
| "ASan es demasiado lento para producción" | Usa GWP-ASan para detección basada en muestreo en producción (~0% de sobrecarga). |
| "Solo usamos contenedores seguros" | La invalidación de iteradores y el acceso no verificado a optional siguen siendo explotables. |
| "Los punteros inteligentes son más lentos" | std::unique_ptr tiene cero sobrecarga en comparación con punteros crudos. Mide antes de afirmar. |
| "Nuestro código no tiene errores de memoria" | Google encontró más de 1000 errores al habilitar hardened libc++. Lo mismo hicieron todos los demás. |
| "Las características de C++26 aún no están disponibles" | Las características de C++20/23 lo están. Las banderas de endurecimiento funcionan con cualquier estándar. Empieza por ahí. |
| "El C++ moderno es más difícil de leer" | std::expected es más legible que verificar códigos de error en 5 parámetros de salida. |
Lista de Verificación de Mejores Prácticas
- Usa punteros inteligentes para la propiedad, punteros crudos solo para observación no propietaria
- Prefiere
std::spanen lugar de puntero + longitud para parámetros de función - Usa
std::expectedpara funciones que pueden fallar con errores tipados - Restringe plantillas con conceptos, no con SFINAE
- Habilita las banderas de endurecimiento del compilador y hardened libc++ en todas las compilaciones
- Ejecuta ASan + UBSan en CI; añade TSan para código concurrente
- Usa
constexpr/constevalcuando sea posible (libre de UB por diseño) - Marca funciones con
[[nodiscard]]cuando ignorar el valor de retorno sea probablemente un error - Prefiere semántica de valor; usa
std::varianten lugar deunion,enum classen lugar deenum - Inicializa todas las variables en el momento de la declaración
Leer a Continuación
- anti-patterns.md — Tabla completa de migración de legado a moderno (30+ patrones)
- cpp20-features.md — Conceptos, ranges, span, format, corrutinas
- cpp23-features.md — expected, print, deducir this, flat_map
- cpp26-features.md — Reflexión, contratos, mejoras de seguridad de memoria
- compiler-hardening.md — Banderas, sanitizadores, hardened libc++
- safe-idioms.md — Patrones de seguridad por clase de vulnerabilidad