c++ - ¿Cuándo usar boost:: opcional y cuándo usar std:: unique_ptr en los casos en los que desea implementar una función que pueda devolver "nada"?
c++11 unique-ptr (7)
Entonces, ¿hay alguna razón para preferir uno sobre el otro?
Expresan una intención muy diferente: optional
dice que la función no puede dar un resultado para darle (y eso no es un caso de error). unique_ptr
le dice algo acerca de la semántica de propiedad (y es más aceptable de usar con null, para expresar un error).
Usualmente usaría el que exprese mejor la intención detrás de la interfaz.
Por ejemplo, considere que estaba escribiendo un servidor HTTP, que intenta analizar un búfer recibido en un objeto de solicitud HTTP. Cuando intenta analizar un búfer incompleto, no hay ningún caso de error, simplemente tiene que esperar y almacenar más datos, e intente nuevamente.
Expresaría esto usando optional
, para dejar claro que la función puede no devolver nada (y no devolver nada no es un caso de error).
En caso de que mi análisis tuviera que validar cosas (por ejemplo, un analizador de expresiones regulares debería producir un error si la expresión analizada no es válida) devolvería un unique_ptr
nulo, o mejor aún, lanzar una excepción.
Por lo que entiendo, hay 2 * formas en que puede implementar una función que a veces no devuelve un resultado (por ejemplo, una persona que se encuentra en una lista de personas).
*: ignoramos la versión ptr sin formato, emparejamos con un indicador bool, y la excepción cuando no encontramos ninguna versión.
boost::optional<Person> findPersonInList();
o
std::unique_ptr<Person> findPersonInList();
Entonces, ¿hay alguna razón para preferir uno sobre el otro?
Ah, ¿Xeo no apareció todavía?
Bueno, esto te lo dije una vez y lo repetiré: estos dos son objetos completamente diferentes con propósitos diferentes.
-
unique_ptr
significa que tengo un objeto . Es solo una forma diferente de decir "Soy un objeto". Por lo tanto, nullunique_ptr
es algo que puede llamar la atención. Esperaba un objeto, pero no conseguí nada; ¡El código debe estar equivocado! -
optional
significa que a veces no se puede inicializar, pero está bien . En este caso, no hay preocupaciones; si no esNone
, es el comportamiento que se ha pensado.
El hecho de que ambos se conviertan implícitamente en bool no significa que se puedan usar de manera intercambiable. Use optional
con el código que probablemente no genere ningún resultado (por ejemplo, leer un flujo). Utilice unique_ptr
en objetos de fábrica; lo más probable es que creen un objeto para usted, y si no, lanzan una excepción.
Una conclusión con respecto a su ejemplo: find
debe devolver optional
.
Conceptualmente se reduce a esto, dado el requisito de ser nullable:
std::optional
tiene valor semántico, pila de almacenamiento.
std::unique_ptr
tiene semántica de movimiento, tienda de almacenamiento dinámico.
Si desea una semántica de valor y una tienda de almacenamiento dinámico, use std::indirect
http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0201r1.pdf .
(Si desea mover la semántica y la pila de tiendas ... No lo sé. ¿Supongo que es un unique_ptr con un asignador de pila?)
Depende: ¿desea devolver un identificador o una copia ?
Si desea devolver un identificador :
-
Person*
-
boost::optional<Person&>
Son dos opciones aceptables. Tiendo a usar una clase Ptr<Person>
que se lanza en caso de acceso nulo, pero esa es mi paranoia.
Si desea devolver una copia :
-
boost::optional<Person>
para clases no polimórficas -
std::unique_ptr<Person>
para clases polimórficas
Debido a que la asignación dinámica incurre en una sobrecarga, por lo que solo se usa cuando es necesario.
Hay una cuarta forma de hacerlo: hacer que la función lance una excepción si no se encuentra nada.
Sé que esto realmente no responde a tu pregunta y, por lo tanto, me disculpo, pero quizás no lo hayas pensado.
La respuesta general es que sus intenciones se expresan con boost::optional
y no con std::unique_ptr
. Dicho esto, su caso especial de una operación de find
probablemente debería ajustarse a la forma estándar de hacerlo de la biblioteca, asumiendo que su tipo subyacente tiene un concepto de iteradores: devuelva un iterador a end()
si no se encuentra ningún elemento, y un iterador a el elemento de lo contrario.
boost::optional
declara más claramente tu intención. std::unique_ptr
documentar explícitamente que std::unique_ptr
vacío significa que no hay valor para devolver