Por lo que tengo entendido, C ++ 14 se introdujo std::make_uniqueporque, como resultado de que no se especificaba el orden de evaluación de parámetros, esto no era seguro:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A(Explicación: si la evaluación primero asigna la memoria para el puntero sin procesar, luego llama g()y se lanza una excepción antes de la std::unique_ptrconstrucción, entonces se pierde la memoria).
Llamar std::make_uniqueera una forma de restringir el orden de las llamadas, haciendo las cosas seguras:
f(std::make_unique<MyClass>(param), g());             // Syntax BDesde entonces, C ++ 17 ha aclarado el orden de evaluación, haciendo que la sintaxis A también sea segura, así que aquí está mi pregunta: ¿todavía hay una razón para usar std::make_uniquemásstd::unique_ptr el constructor de en C ++ 17? ¿Puede dar algunos ejemplos?
A partir de ahora, la única razón que puedo imaginar es que permite escribir MyClasssolo una vez (asumiendo que no necesita depender del polimorfismo con std::unique_ptr<Base>(new Derived(param))). Sin embargo, eso parece una razón bastante débil, especialmente cuando std::make_uniqueno permite especificar un eliminador mientras que std::unique_ptrel constructor de sí lo hace.
Y para ser claros, no estoy abogando a favor de eliminar std::make_uniquede la Biblioteca estándar (mantenerlo tiene sentido al menos para la compatibilidad con versiones anteriores), sino más bien me pregunto si todavía hay situaciones en las que se prefiere fuertementestd::unique_ptr
fuente

std::unique_ptr? No es un argumento en contramake_uniquestd::make_uniqueen primer lugar, no creo que sea razón suficiente para agregarlo al STL, especialmente cuando es una sintaxis que es menos expresiva que usar el constructor, no másRespuestas:
Tienes razón en que se eliminó la razón principal. Todavía hay pautas de no usar nuevas y que son menos razones para escribir (no es necesario repetir el tipo o usar la palabra
new). Es cierto que esos no son argumentos sólidos, pero realmente me gusta no verlosnewen mi código.Además, no te olvides de la coherencia. Definitivamente deberías usarlo
make_sharedpara quemake_uniquesea natural y se ajuste al patrón. Entonces es trivial cambiarstd::make_unique<MyClass>(param)astd::make_shared<MyClass>(param)(o al revés) donde la sintaxis A requiere mucha más reescritura.fuente
new, necesito detenerme y pensar: ¿cuánto tiempo va a vivir este puntero? ¿Lo manejé correctamente? Si hay una excepción, ¿se limpió todo correctamente? Me gustaría no hacerme esas preguntas y perder mi tiempo en eso y si no las usonew, no tengo que hacer esas preguntas.new. ¿No sería maravilloso?std::make_shared- imaginar un caso en el que el objeto asignado es grande y hay una gran cantidad destd::weak_ptrapuntamiento -s a ella: sería mejor dejar que el objeto se eliminará tan pronto como la última compartida El puntero se destruye y vive con solo una pequeña área compartida.std::make_sharedstackoverflow.com/a/20895705/8414561 donde la memoria que se usó para almacenar el objeto no se puede liberar hasta questd::weak_ptrdesaparezca el último (incluso si todosstd::shared_ptr-s apuntan a él (y en consecuencia, el objeto en sí) ya ha sido destruido).make_uniquedistingueTdeT[]yT[N],unique_ptr(new ...)no lo hace.Puede obtener fácilmente un comportamiento indefinido pasando un puntero que fue
new[]ed a aunique_ptr<T>, o pasando un puntero que fuenewed a aunique_ptr<T[]>.fuente
La razón es tener un código más corto sin duplicados. Comparar
Te ahorras
MyClass,newy brackets. Cuesta solo un personaje más en make en comparación con ptr .fuente
MyClass, pero me preguntaba si había una razón más sólida para usarlo<MyClass>pieza en la primera variante.std::unique_ptrque no esté permitida. Tiene que ver con distinguirstd::unique_ptr<T>ystd::unique_ptr<T[]>Cada uso de
newtiene que ser auditado muy cuidadosamente para asegurar que sea correcto de por vida; ¿Se elimina? ¿Sólo una vez?Cada uso de
make_uniqueno lo hace para esas características adicionales; siempre que el objeto propietario tenga una duración "correcta", de forma recursiva hace que el puntero único sea "correcto".Ahora bien, es cierto que
unique_ptr<Foo>(new Foo())es idéntico en todos los aspectos 1 amake_unique<Foo>(); solo requiere un "grep de su código fuente para todos los usos denewpara auditarlos".En realidad, una mentira en el caso general. El reenvío perfecto no es perfecto,
{}init predeterminado, las matrices son todas excepciones.fuente
unique_ptr<Foo>(new Foo)no es del todo idéntico amake_unique<Foo>()... este último sí.new Foo()Pero por lo demás, sí.std::unique_ptrque no esté permitida. Si tiene que ver con distinguirstd::unique_ptr<T>ystd::unique_ptr<T[]>Eso no es lo suficientemente bueno. Confiar en una cláusula técnica recientemente introducida como garantía de seguridad no es una práctica muy sólida:
newen otros lugares, por ejemplo, copiando y pegando su ejemplo.newotra parte (está bien, concedido, no hay muchas posibilidades de eso).Generalmente, es una buena idea que su código sea apropiado / robusto / claramente válido sin tener que recurrir a la disposición de lenguaje, buscar cláusulas técnicas menores u oscuras en el estándar.
(Este es esencialmente el mismo argumento que hice aquí sobre el orden de destrucción de tuplas).
fuente
Considere la función void (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}
Suponga que el nuevo A () tiene éxito, pero el nuevo B () arroja una excepción: lo detecta para reanudar la ejecución normal de su programa. Desafortunadamente, el estándar C ++ no requiere que el objeto A sea destruido y su memoria desasignada: la memoria se filtra silenciosamente y no hay forma de limpiarla. Al envolver A y B en std :: make_uniques, está seguro de que la fuga no ocurrirá:
void function (std :: make_unique (), std :: make_unique ()) {...} El punto aquí es que std :: make_unique y std :: make_unique ahora son objetos temporales, y la limpieza de objetos temporales está correctamente especificada en el estándar C ++: sus destructores se activarán y se liberará la memoria. Entonces, si puede, siempre prefiera asignar objetos usando std :: make_unique y std :: make_shared.
fuente