C++23 se aprobó en 2023 y desde entonces el soporte en los compiladores principales ha avanzado mucho. GCC 14 (publicado en mayo de 2024) cubre prácticamente todo el estándar excepto los módulos. Clang 18 (marzo de 2024) llega a un nivel similar. MSVC 19.4x en Visual Studio 2022 también ha cerrado casi todos los huecos. Lo que antes era papel ahora funciona en producción.
Este artículo recorre las adiciones más útiles de C++23, con código real que puedes compilar hoy. Si quieres saber qué viene después, ya hemos cubierto las novedades de C++26, que incluye reflection, contracts y std::execution.
std::expected: manejo de errores sin excepciones
std::expected<T, E> es probablemente la incorporación más impactante de C++23 para código de producción. Modela el resultado de una operación que puede fallar: o contiene un valor de tipo T, o contiene un error de tipo E. Es la alternativa tipada a lanzar excepciones o devolver códigos de error sueltos.
#include <expected>
#include <string>
#include <charconv>
std::expected<int, std::string> parse_int(std::string_view sv) {
int result{};
auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), result);
if (ec != std::errc{}) {
return std::unexpected(std::string("fallo al parsear: ") + std::string(sv));
}
return result;
}
int main() {
auto val = parse_int("42");
if (val) {
// val.value() == 42
}
auto err = parse_int("abc");
if (!err) {
// err.error() contiene el mensaje
}
}
La ventaja sobre los códigos de error al estilo C es que el compilador te obliga a gestionar el caso de error. La ventaja sobre las excepciones es el coste cero en el camino feliz y la visibilidad en la firma de la función.
std::print y std::println: adiós a printf y cout
C++23 añade std::print y std::println, que combinan el poder de std::format (C++20) con la comodidad de escribir directamente a stdout sin pasar por un objeto ostream.
#include <print>
int main() {
std::println("Hola, {}!", "mundo"); // con salto de línea
std::print("x = {:.2f}n", 3.14159); // sin salto automático
std::println(stderr, "Error en línea {}", 42); // a stderr
}
El formato usa la misma sintaxis que std::format, heredada de la librería {fmt} de Victor Zverovich. Si ya usas std::format, el salto es inmediato.
Deducción de this: el explicit object parameter
Una de las adiciones más elegantes de C++23. Antes, escribir métodos que funcionaran tanto para lvalue como rvalue requería duplicar código o recurrir a plantillas externas. Ahora puedes escribir:
#include <string>
struct Widget {
std::string data;
// El tipo de *this se deduce automáticamente
template <typename Self>
auto&& get(this Self&& self) {
return std::forward<Self>(self).data;
}
};
int main() {
Widget w{"hola"};
auto& ref = w.get(); // Self = Widget&
auto moved = Widget{"bye"}.get(); // Self = Widget&&
}
También simplifica enormemente los patrones CRTP, que antes requerían un static_cast explícito al tipo derivado en cada método.
std::mdspan: vistas multidimensionales
std::mdspan es una vista no propietaria sobre memoria contigua que permite acceder a ella como si fuera un array multidimensional. No copia datos: solo reinterpreta el puntero con la forma que le indiques.
#include <mdspan>
#include <vector>
#include <print>
int main() {
std::vector<int> data(12);
std::iota(data.begin(), data.end(), 0);
// Vista 3x4 sobre el vector
std::mdspan<int, std::extents<size_t, 3, 4>> mat(data.data());
for (size_t i = 0; i < 3; ++i)
for (size_t j = 0; j < 4; ++j)
std::println("mat[{},{}] = {}", i, j, mat[i, j]);
}
Para álgebra lineal, procesado de imágenes o cualquier código que trabaje con buffers multidimensionales, esto elimina la aritmética manual de índices y reduce los bugs de acceso fuera de rango.
std::flat_map y std::flat_set
Los contenedores asociativos ordenados de la STL (std::map, std::set) usan árboles rojo-negro que tienen caché miss alto. C++23 añade std::flat_map y std::flat_set, que internamente usan vectores ordenados y son mucho más amigables con la caché en lecturas frecuentes.
#include <flat_map>
int main() {
std::flat_map<std::string, int> scores;
scores["Alice"] = 95;
scores["Bob"] = 87;
// Internamente: dos vectores paralelos (claves y valores)
// Búsqueda binaria en lugar de árbol
}
Las inserciones y borrados son más lentas que en std::map, así que conviene cuando el ratio lectura/escritura es alto.
std::generator: corrutinas más cómodas
C++20 trajo las corrutinas pero sin tipos de soporte en la biblioteca estándar. C++23 añade std::generator<T>, un tipo listo para usar que modela un generador perezoso:
#include <generator>
#include <print>
std::generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto tmp = a + b;
a = b;
b = tmp;
}
}
int main() {
for (int n : fibonacci() | std::views::take(10)) {
std::print("{} ", n);
}
}
Antes había que escribir el tipo de retorno de corrutina a mano o tirar de librerías externas como cppcoro. Ahora está en el estándar. Para más detalles sobre corrutinas, el siguiente artículo de esta serie las cubre en profundidad.
if consteval
C++23 añade if consteval, que permite distinguir si una función se está evaluando en tiempo de compilación o en tiempo de ejecución:
#include <cmath>
constexpr double sqrt_safe(double x) {
if consteval {
// Ruta en tiempo de compilación
// std::sqrt no es constexpr en todos los compiladores
// Aquí puedes usar tu propia implementación
return x; // simplificado
} else {
return std::sqrt(x); // ruta normal en runtime
}
}
Es un caso de uso muy específico pero resuelve una limitación real que antes obligaba a workarounds con std::is_constant_evaluated().
Cómo activar C++23 en GCC 14 y Clang 18
# GCC 14 g++-14 -std=c++23 -Wall -Wextra tu_archivo.cpp -o salida # Clang 18 clang++-18 -std=c++23 -Wall -Wextra tu_archivo.cpp -o salida # CMake (CMakeLists.txt) # set(CMAKE_CXX_STANDARD 23) # set(CMAKE_CXX_STANDARD_REQUIRED ON)
Algunas características (especialmente std::generator y std::mdspan) pueden requerir flags adicionales o versiones de librería estándar actualizadas. Con libstdc++ de GCC 14 o libc++ de Clang 18 en su versión actual, el soporte es sólido.
Qué queda pendiente
Los módulos siguen siendo el punto débil. GCC 14 tiene soporte experimental, Clang 18 también, pero la integración con sistemas de build como CMake aún tiene aristas. Si los módulos son importantes para tu proyecto, el artículo de esta serie dedicado a ellos entra en los detalles.
Si vienes de Rust o estás evaluando alternativas, también hemos publicado una guía de Rust 2024 edition que comparte muchos conceptos con lo que C++23 intenta resolver.
Imagen: Pexels / Daniil Komov
