¿Por qué std::make unique en lugar de std::unique ptr:: make?


¿Por qué C++ adoptó funciones libres para:

std::make_unique(...);
std::make_shared(...);

En lugar de usar funciones miembro estáticas:

std::unique_ptr::make(...); // static
std::shared_ptr::make(...); // static

?

Author: Mankarse, 2017-03-21

3 answers

TL;DR: Las funciones miembro estáticas siempre tienen acceso a datos privados, pero las funciones libres solo tienen acceso a datos privados cuando se marcan explícitamente como friend. La elección de implementar estas funciones como funciones libres (con un pequeño número implementado como funciones amigas) no es un artefacto histórico aleatorio, sino una decisión deliberada de mejorar la encapsulación mientras se tiene un esquema de nomenclatura consistente para todas las funciones std::make_x.


Hay muchos fábrica estándar funciones en C++:

std::make_pair
std::make_tuple
std::make_unique
std::make_shared //efficiency
std::make_exception_ptr //efficiency
std::make_move_iterator
std::make_reverse_iterator
std::make_error_code
std::make_error_condition
//And several more are proposed for C++17

Para todo lo anterior, la función make_x se puede implementar correctamente utilizando solo la interfaz pública de x. En el caso de make_shared y make_exception_ptr, la aplicación más eficiente requeriría el acceso a los datos internos de std::shared_ptr o std::exception_ptr. Todos los demás se pueden implementar usando solo la interfaz pública con cero penalización de rendimiento.

La implementación de estas funciones como funciones libres no amigas reduce la cantidad de código que tiene acceso a las partes internas privadas del objeto (una propiedad deseable, ya que cuando menos código tiene acceso a datos privados, hay menos lugares que tienen que ser auditados para operaciones que violan los invariantes del objeto, y menos lugares que potencialmente necesitan ser cambiados si las partes internas del objeto cambian).

Si make_shared fuera la única función de fábrica similar, podría tener sentido que sea una función miembro, pero ya que la mayoría de tales funciones no deben ser friend funciones para operar eficientemente, make_shared también se implementa como una función libre, en aras de la coherencia.

Este es el diseño correcto, como si las funciones miembro estático make se utilizaran consistentemente, entonces en todos los casos excepto make_shared y make_exception_ptr, la función miembro inevitablemente tendría un acceso excesivo a los datos privados del objeto x. Con el diseño estandarizado, el pequeño número de make_x funciones que necesitan acceso a datos privados pueden marcarse como friend, y rest respeta la encapsulación correctamente por defecto. Si se usara un miembro no miembro make_x en algunos casos y un miembro estático make en otros, la biblioteca estándar se volvería inconsistente y más difícil de aprender.

 24
Author: Mankarse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 10:38:14

Consistencia.

No creo que haya ninguna razón convincente para tener la sintaxis ::make en lugar de la actual. Asumo que make_unique y make_shared fueron preferidas a una función estática ::make para mantenerse consistentes con las funciones std::make_pair y std::make_heap existentes, que existían antes de C++11.


Tenga en cuenta que std::make_pair tiene una gran ventaja: deduce automáticamente los tipos del par resultante de la llamada a la función:

auto p0 = std::make_pair(1, 1.f); // pair<int, float>

Si tuviéramos std::pair::make, entonces lo haríamos tengo que escribir:

auto p1 = std::pair<int, float>::make(1, 1.f);

Que frustra el propósito de make_pair.


  • Por lo tanto, asumo que make_unique y make_shared fueron elegidos porque los desarrolladores ya estaban acostumbrados a make_pair y funciones similares.

  • make_pair fue elegido en lugar de pair::make para los beneficios antes mencionados.

 17
Author: Vittorio Romeo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 10:45:52

No hay otra razón concreta que no sea la convención - una función de clase estática puede hacer todo lo que una función global puede hacer (en cuanto a funcionalidad).
C++ prefiere funciones globales (para funciones de utilidad) que contienen dentro de un espacio de nombres definido.
Otros lenguajes de programación (como Java) prefieren las funciones públicas estáticas, ya que las funciones globales no son compatibles.

Esto no es nuevo para make_***, existen otros ejemplos:

std::this_thread::XXXX en lugar de std::thread::XXXX_current
aunque podría tenía sentido poner la función que se relaciona con el hilo de ejecución actual como funciones estáticas dentro de la clase thread, se hacen globales dentro del espacio de nombres this_thread.

También, podríamos tener algo como std::container::sort que std::container es una clase auxiliar para contenedores, pero tenemos std::sort en su lugar.

 3
Author: David Haim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 09:53:05