C++23 y Vista Previa de C++26
Explora std::expected de C++23, std::print, deducing this, mejoras de ranges y una vista previa de las proximas caracteristicas de C++26 como contratos, reflexion y pattern matching.
La Evolucion Continua
C++23 es una version de "completado" que llena vacios dejados por C++20 y agrega caracteristicas muy solicitadas. std::expected trae el manejo de errores al estilo Rust, std::print proporciona salida moderna, deducing this habilita patrones poderosos de metaprogramacion, y ranges gana docenas de nuevas vistas. Mientras tanto, C++26 se perfila como otra version importante con contratos, reflexion y pattern matching — caracteristicas que cambiaran fundamentalmente como escribimos codigo C++ seguro y expresivo.
std::expected: Manejo Moderno de Errores
std::expected contiene un valor de exito de tipo T o un error de tipo E. Combinado con operaciones monadicas (.and_then(), .transform(), .or_else()), habilita manejo de errores componible sin excepciones:
#include <iostream>
#include <expected>
#include <string>
#include <charconv>
#include <system_error>
enum class ParseError {
empty_input,
invalid_format,
out_of_range
};
std::expected<int, ParseError> parse_int(std::string_view sv) {
if (sv.empty()) return std::unexpected(ParseError::empty_input);
int result{};
auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), result);
if (ec == std::errc::invalid_argument)
return std::unexpected(ParseError::invalid_format);
if (ec == std::errc::result_out_of_range)
return std::unexpected(ParseError::out_of_range);
if (ptr != sv.data() + sv.size())
return std::unexpected(ParseError::invalid_format);
return result;
}
std::expected<int, ParseError> double_if_positive(int val) {
if (val <= 0) return std::unexpected(ParseError::out_of_range);
return val * 2;
}
int main() {
// Direct usage
auto result = parse_int("42");
if (result) {
std::cout << "Parsed: " << *result << "\n";
} else {
std::cout << "Error code: " << static_cast<int>(result.error()) << "\n";
}
// Monadic chaining — like Rust's Result::and_then
auto chained = parse_int("21")
.and_then(double_if_positive) // int -> expected<int, E>
.transform([](int v) { return v + 1; }) // int -> int (wrapped)
.or_else([](ParseError e) -> std::expected<int, ParseError> {
std::cout << "Recovering from error\n";
return 0; // default value on error
});
std::cout << "Chained result: " << *chained << "\n"; // 43
return 0;
}std::print y std::println
std::print y std::println combinan std::format con salida, reemplazando std::cout << para la mayoria de los casos de uso. Son mas rapidos, con seguridad de tipos y producen codigo legible:
#include <print>
#include <vector>
#include <string>
#include <ranges>
int main() {
// Basic printing — no more << chains!
std::println("Hello, {}!", "C++23");
std::print("No newline here. ");
std::println("But here.");
// Formatting works just like std::format
int x = 42;
double pi = 3.14159;
std::println("x = {}, pi = {:.2f}", x, pi);
std::println("Hex: {:#x}, Binary: {:#b}", 255, 255);
// Print to stderr
std::println(stderr, "Warning: something happened");
// Containers and ranges (C++23 formatter support)
std::vector<int> v{1, 2, 3, 4, 5};
std::println("Vector: {}", v); // [1, 2, 3, 4, 5]
// Formatted table
std::println("{:<15} {:>10} {:>10}", "Name", "Score", "Grade");
std::println("{:<15} {:>10} {:>10}", "Alice", 95, "A");
std::println("{:<15} {:>10} {:>10}", "Bob", 87, "B+");
return 0;
}Mejoras de Ranges: zip, chunk, slide
C++23 agrega nuevas y poderosas vistas de range que cubren patrones comunes que anteriormente requerian bucles manuales o bibliotecas de terceros:
#include <print>
#include <ranges>
#include <vector>
#include <string>
int main() {
std::vector<std::string> names{"Alice", "Bob", "Carol"};
std::vector<int> scores{95, 87, 92};
// zip — combine multiple ranges element-wise
for (auto [name, score] : std::views::zip(names, scores)) {
std::println("{}: {}", name, score);
}
std::vector<int> data{1, 2, 3, 4, 5, 6, 7, 8, 9};
// chunk — split into groups of N
for (auto chunk : data | std::views::chunk(3)) {
std::print("[ ");
for (int v : chunk) std::print("{} ", v);
std::println("]");
}
// [ 1 2 3 ] [ 4 5 6 ] [ 7 8 9 ]
// slide — sliding window of size N
for (auto window : data | std::views::slide(3)) {
std::print("( ");
for (int v : window) std::print("{} ", v);
std::println(")");
}
// ( 1 2 3 ) ( 2 3 4 ) ( 3 4 5 ) ... ( 7 8 9 )
// cartesian_product — all combinations
std::vector<char> suits{'H', 'D', 'C', 'S'};
std::vector<int> ranks{1, 2, 3};
for (auto [suit, rank] : std::views::cartesian_product(suits, ranks)) {
std::print("{}{} ", suit, rank);
}
std::println("");
// enumerate — index + value (finally!)
for (auto [i, name] : std::views::enumerate(names)) {
std::println("[{}] {}", i, name);
}
return 0;
}Deducing this
El deducing this de C++23 (tambien llamado "parametro de objeto explicito") permite que las funciones miembro tomen this como un parametro explicito y deducido. Esto elimina la necesidad de duplicar sobrecargas const/no-const, habilita lambdas recursivos y simplifica CRTP:
struct Widget {
std::string name;
// Antes de C++23: dos sobrecargas identicas
// const std::string& get_name() const { return name; }
// std::string& get_name() { return name; }
// C++23: una funcion maneja ambos casos
template<typename Self>
auto&& get_name(this Self&& self) {
return std::forward<Self>(self).name;
}
};El parametro this Self&& self deduce si el objeto es const, no-const, lvalue o rvalue — y reenvía correctamente. Este patron, llamado el idioma "deducing this", puede reducir a la mitad los conjuntos de sobrecargas de funciones miembro.
Vista Previa de C++26: Contratos, Reflexion, Pattern Matching
Se espera que C++26 incluya varias caracteristicas transformadoras:
Contratos — pre, post y contract_assert permiten precondiciones, postcondiciones y aserciones que el sistema de compilacion puede habilitar o deshabilitar:
int sqrt_int(int x)
pre(x >= 0)
post(r: r * r <= x)
{
// implementacion
}Reflexion Estatica — Inspecciona tipos, miembros y enumeradores en tiempo de compilacion. Genera codigo basado en campos de structs, auto-deriva serializacion y mas — todo sin macros.
Pattern Matching — Las expresiones inspect permiten hacer coincidencia sobre tipos, valores y estructura:
inspect (variant_value) {
<int> i => std::println("int: {}", i);
<string> s => std::println("string: {}", s);
__ => std::println("other");
};Sender/Receiver (std::execution) — Un framework estandar para ejecucion asincrona que proporciona concurrencia estructurada, reemplazando implementaciones ad-hoc de pools de hilos.
Manteniendote al dia con la evolucion de C++:
1. Apunta a un estandar minimo para tu proyecto (C++17 es una base solida para la mayoria del codigo en produccion hoy)
2. Usa flags del compilador para seleccionar el estandar: -std=c++17, -std=c++20, -std=c++23
3. Verifica el soporte del compilador en cppreference.com/compiler_support — no todos los compiladores implementan cada caracteristica
4. Usa macros de prueba de caracteristicas como __cpp_concepts, __cpp_lib_expected para usar condicionalmente caracteristicas mas nuevas
5. Lee las propuestas (wg21.link/pXXXX) para las caracteristicas que planeas adoptar
6. No persigas cada nueva caracteristica — adopta lo que hace tu base de codigo mas simple y mas correcta, no solo mas nueva
Adoptar las caracteristicas del ultimo estandar demasiado pronto tiene costos reales:
Bugs del compilador: Las nuevas caracteristicas frecuentemente tienen errores de implementacion que solo se descubren a traves del uso en produccion. Los modules y coroutines de C++20, por ejemplo, tuvieron bugs significativos en los compiladores durante 2-3 anos despues de la estandarizacion.
Herramientas incompletas: Los depuradores, profilers y analizadores estaticos pueden no soportar completamente las caracteristicas mas nuevas. El soporte del IDE (IntelliSense, autocompletado de codigo) frecuentemente se queda atras.
Portabilidad: Si tu codigo debe compilar en multiples plataformas (Linux, macOS, Windows, embebidos), estas limitado a la interseccion de lo que todos los compiladores objetivo soportan.
Preparacion del equipo: Las nuevas caracteristicas requieren aprendizaje de todo el equipo. Una caracteristica que solo un desarrollador entiende crea un cuello de botella de mantenimiento.
std::expectedhabilita manejo de errores al estilo Rust con encadenamiento monadico (.and_then(),.transform())std::print/std::printlnreemplazanstd::cout <<con salida formateada limpia, rapida y con seguridad de tipos- Los ranges de C++23 agregan vistas
zip,chunk,slide,cartesian_productyenumerate - Deducing this elimina la duplicacion de sobrecargas de funciones miembro const/no-const
- C++26 apunta a contratos, reflexion estatica, pattern matching y
std::executionpara asincronia - Adopta nuevos estandares incrementalmente — verifica el soporte del compilador, prueba exhaustivamente y asegura la preparacion del equipo
Quiz — Pon a prueba tus conocimientos
(15 XP)1. Que representa `std::expected<int, Error>`?
2. Que produce `std::views::zip(names, scores)`?
3. Que caracteristica de C++26 permite especificar precondiciones y postcondiciones de funciones?