Última actualización:2026-10-12
C++17 optional_ref¶
Este artículo reseña el blog de foonathan optional_ref discussion; el original es la mejor discusión sobre por qué std::optional<T&> (una referencia opcional) nunca entró en el estándar. El tema volvió a la mesa justo antes de C++26, así que vale la pena repasar los argumentos centrales.
1. El problema: ¿por qué std::optional no compila?¶
std::optional<int>, std::optional<std::string> funcionan sin problemas, pero std::optional<int&> da error de compilación directamente. La razón es algo «histórica»: al elaborar el estándar no se llegó a un consenso sobre sus semánticas de asignación, así que sencillamente se escribió en el estándar que el comportamiento es indefinido cuando T es un tipo referencia.
¿Y qué tiene de tan difícil una «referencia que quizá esté vacía»? El autor destila cuatro preguntas:
(1) ¿Es optional<T&> lo mismo que un puntero?
(2) ¿Realmente necesitamos optional<T&>?
(3) Al asignar, ¿debe hacer rebind (revincularse al nuevo objeto) o assign-through (escribir en el objeto apuntado)?
(4) ¿Debería tener operador de asignación en absoluto?
Las respuestas tl;dr del autor: no, no, rebind, y no debería. Veamos por qué.
2. Una referencia opcional no es un puntero¶
Intuitivamente, una «referencia posiblemente vacía» es un puntero, pero el autor señala una diferencia más esencial:
- la creación y el acceso de
T&son ambos implícitos: con la referencia en mano se usa directamente, sin desreferenciar; - la creación y el acceso de
T*son ambos explícitos: hace falta&xpara tomar la dirección y*ppara desreferenciar.
Esta diferencia de explicitud tiene valor: un puntero te recuerda que «aquí podría estar vacío, podría haber problemas de aliasing». Convertir optional<T&> en un «puntero más cómodo» con conversiones implícitas por todas partes tira precisamente esa capa de protección. La conclusión del autor: si quieres expresar «posiblemente vacío», un T* explícito es incluso mejor documentación.
3. ¿rebind o assign-through?¶
Este es el nudo central de toda la discusión:
Dos lecturas:
- rebind: opt queda ahora vinculado a b (la semántica de alias de las referencias;
int& ra = a; ra = b;asigna b a a… espera, ¡la asignación deint&es justamente assign-through!) - assign-through: escribir el valor de b en a (la semántica de una referencia normal
T&).
Ahí está la contradicción: si la referencia opcional quiere alinearse con la semántica de T&, la asignación debería ser assign-through; pero un optional mutable (que puede volver a contener otro objeto) intuitivamente exige rebind. Ambas semánticas son razonables, el comité no logró ponerse de acuerdo, y así optional<T&> nunca se legalizó.
En la práctica, cuando se quiere semántica de rebind, la solución de la comunidad es fabricar el propio optional_ref<T>: internamente guarda un T*, y asignar es cambiar de puntero, con una semántica clarísima. La propuesta de std::optional_ref de C++26 sigue el mismo camino.
4. Resumen¶
(1) optional<T&> no es azúcar sintáctico del puntero: ambos tienen una división esencial del trabajo en cuanto a explicitud;
(2) lo que lo bloqueó es la ambigüedad de la semántica de asignación: tanto rebind como assign-through son defendibles;
(3) cuando se necesita una «referencia anulable», un puntero explícito o un optional_ref son opciones más seguras que un «optional
Para terminar, una pregunta: ¿deberían value() y *opt de optional<T&> devolver T& o const T&? Y si también se quiere expresar la semántica de «capturado como const», ¿cómo diseñar el parámetro de plantilla?
Temas relacionados¶
- C++17: el atributo [[nodiscard]] | C++那些事
- C++17 std::string_view | C++那些事
- Recursos de memoria PMR en C++17 | C++那些事
- Diecisiete características avanzadas de C++17 | C++那些事
- Deducción de argumentos de plantilla de clase CTAD en C++17 | C++那些事
- C++17 std::variant | C++那些事
- Vinculaciones estructuradas en C++17 | C++那些事
- Deducción con auto en plantillas de C++17 | C++那些事
- Plantillas de variable en C++17 | C++那些事